2008-11-04 21:38:18

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 0/5] eCryptfs: Filename Encryption

This patchset implements filename encryption via a passphrase-derived
mount-wide Filename Encryption Key (FNEK) specified as a mount
parameter. Each encrypted filename has a fixed prefix indicating that
eCryptfs should try to decrypt the filename. When eCryptfs encounters
this prefix, it decodes the filename into a tag 70 packet and then
decrypts the packet contents using the FNEK, setting the filename to
the decrypted filename. Both unencrypted and encrypted filenames can
reside in the same lower filesystem.

Because filename encryption expands the length of the filename during
the encoding stage, eCryptfs will not properly handle filenames that
are already near the maximum filename length.

In the present implementation, eCryptfs must be able to produce a
match against the lower encrypted and encoded filename representation
when given a plaintext filename. Therefore, two files having the same
plaintext name will encrypt and encode into the same lower filename if
they are both encrypted using the same FNEK. This can be changed by
finding a way to replace the prepended bytes in the blocked-aligned
filename with random characters; they are hashes of the FNEK right
now, so that it is possible to deterministically map from a plaintext
filename to an encrypted and encoded filename in the lower
filesystem. An implementation using random characters will have to
decode and decrypt every single directory entry in any given directory
any time an event occurs wherein the VFS needs to determine whether a
particular file exists in the lower directory and the decrypted and
decoded filenames have not yet been extracted for that directory.

Thanks to Tyler Hicks and David Kleikamp for assistance in the
development of this patchset.

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


2008-11-04 21:39:25

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 1/5] eCryptfs: Filename Encryption: Tag 70 packets

A tag 70 packet contains a filename encrypted with a Filename Encryption
Key (FNEK). This patch implements functions for writing and parsing tag
70 packets. This patch also adds definitions and extends structures to
support filename encryption.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 11 +-
fs/ecryptfs/ecryptfs_kernel.h | 38 +++-
fs/ecryptfs/keystore.c | 634 ++++++++++++++++++++++++++++++++++++----
3 files changed, 613 insertions(+), 70 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 6046239..4857327 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1149,19 +1149,20 @@ ecryptfs_cipher_code_str_map[] = {

/**
* ecryptfs_code_for_cipher_string
- * @crypt_stat: The cryptographic context
+ * @cipher_name: The string alias for the cipher
+ * @key_bytes: Length of key in bytes; used for AES code selection
*
* Returns zero on no match, or the cipher code on match
*/
-u8 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat)
+u8 ecryptfs_code_for_cipher_string(char *cipher_name, size_t key_bytes)
{
int i;
u8 code = 0;
struct ecryptfs_cipher_code_str_map_elem *map =
ecryptfs_cipher_code_str_map;

- if (strcmp(crypt_stat->cipher, "aes") == 0) {
- switch (crypt_stat->key_size) {
+ if (strcmp(cipher_name, "aes") == 0) {
+ switch (key_bytes) {
case 16:
code = RFC2440_CIPHER_AES_128;
break;
@@ -1173,7 +1174,7 @@ u8 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat)
}
} else {
for (i = 0; i < ARRAY_SIZE(ecryptfs_cipher_code_str_map); i++)
- if (strcmp(crypt_stat->cipher, map[i].cipher_str) == 0){
+ if (strcmp(cipher_name, map[i].cipher_str) == 0) {
code = map[i].cipher_code;
break;
}
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index a75026d..76a95bd 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -199,6 +199,7 @@ ecryptfs_get_key_payload_data(struct key *key)
#define ECRYPTFS_DEFAULT_CIPHER "aes"
#define ECRYPTFS_DEFAULT_KEY_BYTES 16
#define ECRYPTFS_DEFAULT_HASH "md5"
+#define ECRYPTFS_TAG_70_DIGEST ECRYPTFS_DEFAULT_HASH
#define ECRYPTFS_TAG_1_PACKET_TYPE 0x01
#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C
#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED
@@ -206,7 +207,25 @@ ecryptfs_get_key_payload_data(struct key *key)
#define ECRYPTFS_TAG_65_PACKET_TYPE 0x41
#define ECRYPTFS_TAG_66_PACKET_TYPE 0x42
#define ECRYPTFS_TAG_67_PACKET_TYPE 0x43
+#define ECRYPTFS_TAG_70_PACKET_TYPE 0x46 /* FNEK-encrypted filename
+ * as dentry name */
+#define ECRYPTFS_TAG_71_PACKET_TYPE 0x47 /* FNEK-encrypted filename in
+ * metadata */
+#define ECRYPTFS_TAG_72_PACKET_TYPE 0x48 /* FEK-encrypted filename as
+ * dentry name */
+#define ECRYPTFS_TAG_73_PACKET_TYPE 0x49 /* FEK-encrypted filename as
+ * metadata */
+/* Constraint: ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES >=
+ * ECRYPTFS_MAX_IV_BYTES */
+#define ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES 16
+#define ECRYPTFS_NON_NULL 0x42 /* A reasonable substitute for NULL */
#define MD5_DIGEST_SIZE 16
+#define ECRYPTFS_TAG_70_DIGEST_SIZE MD5_DIGEST_SIZE
+#define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FEK_ENCRYPTED."
+#define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX_SIZE 23
+#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FNEK_ENCRYPTED."
+#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE 24
+#define ECRYPTFS_ENCRYPTED_DENTRY_NAME_LEN (18 + 1 + 4 + 1 + 32)

struct ecryptfs_key_sig {
struct list_head crypt_stat_list;
@@ -332,13 +351,20 @@ struct ecryptfs_mount_crypt_stat {
#define ECRYPTFS_XATTR_METADATA_ENABLED 0x00000002
#define ECRYPTFS_ENCRYPTED_VIEW_ENABLED 0x00000004
#define ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED 0x00000008
+#define ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES 0x00000010
+#define ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK 0x00000020
+#define ECRYPTFS_GLOBAL_ENCFN_USE_FEK 0x00000040
u32 flags;
struct list_head global_auth_tok_list;
struct mutex global_auth_tok_list_mutex;
size_t num_global_auth_toks;
size_t global_default_cipher_key_size;
+ size_t global_default_fn_cipher_key_bytes;
unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE
+ 1];
+ unsigned char global_default_fn_cipher_name[
+ ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
+ char global_default_fnek_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
};

/* superblock private data. */
@@ -599,7 +625,7 @@ int ecryptfs_read_and_validate_header_region(char *data,
struct inode *ecryptfs_inode);
int ecryptfs_read_and_validate_xattr_region(char *page_virt,
struct dentry *ecryptfs_dentry);
-u8 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat);
+u8 ecryptfs_code_for_cipher_string(char *cipher_name, size_t key_bytes);
int ecryptfs_cipher_code_to_string(char *str, u8 cipher_code);
void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat);
int ecryptfs_generate_key_packet_set(char *dest_base,
@@ -694,5 +720,15 @@ int ecryptfs_privileged_open(struct file **lower_file,
struct vfsmount *lower_mnt,
const struct cred *cred);
int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry);
+int
+ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
+ size_t *packet_size,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ char *filename, size_t filename_size);
+int
+ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
+ size_t *packet_size,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ char *data, size_t max_packet_size);

#endif /* #ifndef ECRYPTFS_KERNEL_H */
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index e22bc39..1e16893 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -403,6 +403,569 @@ out:
}

static int
+ecryptfs_find_global_auth_tok_for_sig(
+ struct ecryptfs_global_auth_tok **global_auth_tok,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig)
+{
+ struct ecryptfs_global_auth_tok *walker;
+ int rc = 0;
+
+ (*global_auth_tok) = NULL;
+ mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
+ list_for_each_entry(walker,
+ &mount_crypt_stat->global_auth_tok_list,
+ mount_crypt_stat_list) {
+ if (memcmp(walker->sig, sig, ECRYPTFS_SIG_SIZE_HEX) == 0) {
+ (*global_auth_tok) = walker;
+ goto out;
+ }
+ }
+ rc = -EINVAL;
+out:
+ mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
+ return rc;
+}
+
+/**
+ * ecryptfs_find_auth_tok_for_sig
+ * @auth_tok: Set to the matching auth_tok; NULL if not found
+ * @crypt_stat: inode crypt_stat crypto context
+ * @sig: Sig of auth_tok to find
+ *
+ * For now, this function simply looks at the registered auth_tok's
+ * linked off the mount_crypt_stat, so all the auth_toks that can be
+ * used must be registered at mount time. This function could
+ * potentially try a lot harder to find auth_tok's (e.g., by calling
+ * out to ecryptfsd to dynamically retrieve an auth_tok object) so
+ * that static registration of auth_tok's will no longer be necessary.
+ *
+ * Returns zero on no error; non-zero on error
+ */
+static int
+ecryptfs_find_auth_tok_for_sig(
+ struct ecryptfs_auth_tok **auth_tok,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ char *sig)
+{
+ struct ecryptfs_global_auth_tok *global_auth_tok;
+ int rc = 0;
+
+ (*auth_tok) = NULL;
+ if (ecryptfs_find_global_auth_tok_for_sig(&global_auth_tok,
+ mount_crypt_stat, sig)) {
+ struct key *auth_tok_key;
+
+ rc = ecryptfs_keyring_auth_tok_for_sig(&auth_tok_key, auth_tok,
+ sig);
+ } else
+ (*auth_tok) = global_auth_tok->global_auth_tok;
+ return rc;
+}
+
+/**
+ * write_tag_70_packet can gobble a lot of stack space. We stuff most
+ * of the function's parameters in a kmalloc'd struct to help reduce
+ * eCryptfs' overall stack usage.
+ */
+struct ecryptfs_write_tag_70_packet_silly_stack {
+ u8 cipher_code;
+ size_t max_packet_size;
+ size_t packet_size_len;
+ size_t block_aligned_filename_size;
+ size_t block_size;
+ size_t i;
+ size_t j;
+ size_t num_rand_bytes;
+ struct mutex *tfm_mutex;
+ char *block_aligned_filename;
+ struct ecryptfs_auth_tok *auth_tok;
+ struct scatterlist src_sg;
+ struct scatterlist dst_sg;
+ struct blkcipher_desc desc;
+ char iv[ECRYPTFS_MAX_IV_BYTES];
+ char hash[ECRYPTFS_TAG_70_DIGEST_SIZE];
+ char tmp_hash[ECRYPTFS_TAG_70_DIGEST_SIZE];
+ struct hash_desc hash_desc;
+ struct scatterlist hash_sg;
+};
+
+/**
+ * write_tag_70_packet - Write encrypted filename (EFN) packet against FNEK
+ * @filename: NULL-terminated filename string
+ *
+ * This is the simplest mechanism for achieving filename encryption in
+ * eCryptfs. It encrypts the given filename with the mount-wide
+ * filename encryption key (FNEK) and stores it in a packet to @dest,
+ * which the callee will encode and write directly into the dentry
+ * name.
+ */
+int
+ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
+ size_t *packet_size,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ char *filename, size_t filename_size)
+{
+ struct ecryptfs_write_tag_70_packet_silly_stack *s;
+ int rc = 0;
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s) {
+ printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
+ "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
+ goto out;
+ }
+ s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ (*packet_size) = 0;
+ rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(
+ &s->desc.tfm,
+ &s->tfm_mutex, mount_crypt_stat->global_default_fn_cipher_name);
+ if (unlikely(rc)) {
+ printk(KERN_ERR "Internal error whilst attempting to get "
+ "tfm and mutex for cipher name [%s]; rc = [%d]\n",
+ mount_crypt_stat->global_default_fn_cipher_name, rc);
+ goto out;
+ }
+ mutex_lock(s->tfm_mutex);
+ s->block_size = crypto_blkcipher_blocksize(s->desc.tfm);
+ /* Plus one for the \0 separator between the random prefix
+ * and the plaintext filename */
+ s->num_rand_bytes = (ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES + 1);
+ s->block_aligned_filename_size = (s->num_rand_bytes + filename_size);
+ if ((s->block_aligned_filename_size % s->block_size) != 0) {
+ s->num_rand_bytes += (s->block_size
+ - (s->block_aligned_filename_size
+ % s->block_size));
+ s->block_aligned_filename_size = (s->num_rand_bytes
+ + filename_size);
+ }
+ /* Octet 0: Tag 70 identifier
+ * Octets 1-N1: Tag 70 packet size (includes cipher identifier
+ * and block-aligned encrypted filename size)
+ * Octets N1-N2: FNEK sig (ECRYPTFS_SIG_SIZE)
+ * Octet N2-N3: Cipher identifier (1 octet)
+ * Octets N3-N4: Block-aligned encrypted filename
+ * - Consists of a minimum number of random characters, a \0
+ * separator, and then the filename */
+ s->max_packet_size = (1 /* Tag 70 identifier */
+ + 3 /* Max Tag 70 packet size */
+ + ECRYPTFS_SIG_SIZE /* FNEK sig */
+ + 1 /* Cipher identifier */
+ + s->block_aligned_filename_size);
+ if (dest == NULL) {
+ (*packet_size) = s->max_packet_size;
+ goto out_unlock;
+ }
+ if (s->max_packet_size > (*remaining_bytes)) {
+ printk(KERN_WARNING "%s: Require [%d] bytes to write; only "
+ "[%d] available\n", __func__, s->max_packet_size,
+ (*remaining_bytes));
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ s->block_aligned_filename = kzalloc(s->block_aligned_filename_size,
+ GFP_KERNEL);
+ if (!s->block_aligned_filename) {
+ printk(KERN_ERR "%s: Out of kernel memory whilst attempting to "
+ "kzalloc [%Zd] bytes\n", __func__,
+ s->block_aligned_filename_size);
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+ s->i = 0;
+ dest[s->i++] = ECRYPTFS_TAG_70_PACKET_TYPE;
+ rc = ecryptfs_write_packet_length(&dest[s->i],
+ (ECRYPTFS_SIG_SIZE
+ + 1 /* Cipher code */
+ + s->block_aligned_filename_size),
+ &s->packet_size_len);
+ if (rc) {
+ printk(KERN_ERR "%s: Error generating tag 70 packet "
+ "header; cannot generate packet length; rc = [%d]\n",
+ __func__, rc);
+ goto out_free_unlock;
+ }
+ s->i += s->packet_size_len;
+ ecryptfs_from_hex(&dest[s->i],
+ mount_crypt_stat->global_default_fnek_sig,
+ ECRYPTFS_SIG_SIZE);
+ s->i += ECRYPTFS_SIG_SIZE;
+ s->cipher_code = ecryptfs_code_for_cipher_string(
+ mount_crypt_stat->global_default_fn_cipher_name,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ if (s->cipher_code == 0) {
+ printk(KERN_WARNING "%s: Unable to generate code for "
+ "cipher [%s] with key bytes [%d]\n", __func__,
+ mount_crypt_stat->global_default_fn_cipher_name,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ rc = -EINVAL;
+ goto out_free_unlock;
+ }
+ dest[s->i++] = s->cipher_code;
+ rc = ecryptfs_find_auth_tok_for_sig(
+ &s->auth_tok, mount_crypt_stat,
+ mount_crypt_stat->global_default_fnek_sig);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to find auth tok for "
+ "fnek sig [%s]; rc = [%d]\n", __func__,
+ mount_crypt_stat->global_default_fnek_sig, rc);
+ goto out_free_unlock;
+ }
+ /* TODO: Support other key modules than passphrase for
+ * filename encryption */
+ BUG_ON(s->auth_tok->token_type != ECRYPTFS_PASSWORD);
+ sg_init_one(
+ &s->hash_sg,
+ (u8 *)s->auth_tok->token.password.session_key_encryption_key,
+ s->auth_tok->token.password.session_key_encryption_key_bytes);
+ s->hash_desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ s->hash_desc.tfm = crypto_alloc_hash(ECRYPTFS_TAG_70_DIGEST, 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(s->hash_desc.tfm)) {
+ rc = PTR_ERR(s->hash_desc.tfm);
+ printk(KERN_ERR "%s: Error attempting to "
+ "allocate hash crypto context; rc = [%d]\n",
+ __func__, rc);
+ goto out_free_unlock;
+ }
+ rc = crypto_hash_init(&s->hash_desc);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error initializing crypto hash; rc = [%d]\n",
+ __func__, rc);
+ goto out_release_free_unlock;
+ }
+ rc = crypto_hash_update(
+ &s->hash_desc, &s->hash_sg,
+ s->auth_tok->token.password.session_key_encryption_key_bytes);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error updating crypto hash; rc = [%d]\n",
+ __func__, rc);
+ goto out_release_free_unlock;
+ }
+ rc = crypto_hash_final(&s->hash_desc, s->hash);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error finalizing crypto hash; rc = [%d]\n",
+ __func__, rc);
+ goto out_release_free_unlock;
+ }
+ for (s->j = 0; s->j < (s->num_rand_bytes - 1); s->j++) {
+ s->block_aligned_filename[s->j] =
+ s->hash[(s->j % ECRYPTFS_TAG_70_DIGEST_SIZE)];
+ if ((s->j % ECRYPTFS_TAG_70_DIGEST_SIZE)
+ == (ECRYPTFS_TAG_70_DIGEST_SIZE - 1)) {
+ sg_init_one(&s->hash_sg, (u8 *)s->hash,
+ ECRYPTFS_TAG_70_DIGEST_SIZE);
+ rc = crypto_hash_init(&s->hash_desc);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error initializing crypto hash; "
+ "rc = [%d]\n", __func__, rc);
+ goto out_release_free_unlock;
+ }
+ rc = crypto_hash_update(&s->hash_desc, &s->hash_sg,
+ ECRYPTFS_TAG_70_DIGEST_SIZE);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error updating crypto hash; "
+ "rc = [%d]\n", __func__, rc);
+ goto out_release_free_unlock;
+ }
+ rc = crypto_hash_final(&s->hash_desc, s->tmp_hash);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: Error finalizing crypto hash; "
+ "rc = [%d]\n", __func__, rc);
+ goto out_release_free_unlock;
+ }
+ memcpy(s->hash, s->tmp_hash,
+ ECRYPTFS_TAG_70_DIGEST_SIZE);
+ }
+ if (s->block_aligned_filename[s->j] == '\0')
+ s->block_aligned_filename[s->j] = ECRYPTFS_NON_NULL;
+ }
+ memcpy(&s->block_aligned_filename[s->num_rand_bytes], filename,
+ filename_size);
+ rc = virt_to_scatterlist(s->block_aligned_filename,
+ s->block_aligned_filename_size, &s->src_sg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
+ "convert filename memory to scatterlist; "
+ "expected rc = 1; got rc = [%d]. "
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
+ s->block_aligned_filename_size);
+ goto out_release_free_unlock;
+ }
+ rc = virt_to_scatterlist(&dest[s->i], s->block_aligned_filename_size,
+ &s->dst_sg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
+ "convert encrypted filename memory to scatterlist; "
+ "expected rc = 1; got rc = [%d]. "
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
+ s->block_aligned_filename_size);
+ goto out_release_free_unlock;
+ }
+ /* The characters in the first block effectively do the job
+ * of the IV here, so we just use 0's for the IV. Note the
+ * constraint that ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES
+ * >= ECRYPTFS_MAX_IV_BYTES. */
+ memset(s->iv, 0, ECRYPTFS_MAX_IV_BYTES);
+ s->desc.info = s->iv;
+ rc = crypto_blkcipher_setkey(
+ s->desc.tfm,
+ s->auth_tok->token.password.session_key_encryption_key,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: Error setting key for crypto context; "
+ "rc = [%d]. s->auth_tok->token.password.session_key_"
+ "encryption_key = [0x%p]; mount_crypt_stat->"
+ "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
+ rc,
+ s->auth_tok->token.password.session_key_encryption_key,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ goto out_release_free_unlock;
+ }
+ rc = crypto_blkcipher_encrypt_iv(&s->desc, &s->dst_sg, &s->src_sg,
+ s->block_aligned_filename_size);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to encrypt filename; "
+ "rc = [%d]\n", __func__, rc);
+ goto out_release_free_unlock;
+ }
+ s->i += s->block_aligned_filename_size;
+ (*packet_size) = s->i;
+ (*remaining_bytes) -= (*packet_size);
+out_release_free_unlock:
+ crypto_free_hash(s->hash_desc.tfm);
+out_free_unlock:
+ memset(s->block_aligned_filename, 0, s->block_aligned_filename_size);
+ kfree(s->block_aligned_filename);
+out_unlock:
+ mutex_unlock(s->tfm_mutex);
+out:
+ kfree(s);
+ return rc;
+}
+
+struct ecryptfs_parse_tag_70_packet_silly_stack {
+ u8 cipher_code;
+ size_t max_packet_size;
+ size_t packet_size_len;
+ size_t parsed_tag_70_packet_size;
+ size_t block_aligned_filename_size;
+ size_t block_size;
+ size_t i;
+ struct mutex *tfm_mutex;
+ char *decrypted_filename;
+ struct ecryptfs_auth_tok *auth_tok;
+ struct scatterlist src_sg;
+ struct scatterlist dst_sg;
+ struct blkcipher_desc desc;
+ char fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX + 1];
+ char iv[ECRYPTFS_MAX_IV_BYTES];
+ char cipher_string[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+};
+
+/**
+ * parse_tag_70_packet - Parse and process FNEK-encrypted passphrase packet
+ * @filename: This function kmalloc's the memory for the filename
+ */
+int
+ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
+ size_t *packet_size,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ char *data, size_t max_packet_size)
+{
+ struct ecryptfs_parse_tag_70_packet_silly_stack *s;
+ int rc = 0;
+
+ (*packet_size) = 0;
+ (*filename_size) = 0;
+ (*filename) = NULL;
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s) {
+ printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
+ "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
+ goto out;
+ }
+ s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ if (max_packet_size < (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)) {
+ printk(KERN_WARNING "%s: max_packet_size is [%Zd]; it must be "
+ "at least [%d]\n", __func__, max_packet_size,
+ (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1));
+ rc = -EINVAL;
+ goto out;
+ }
+ /* Octet 0: Tag 70 identifier
+ * Octets 1-N1: Tag 70 packet size (includes cipher identifier
+ * and block-aligned encrypted filename size)
+ * Octets N1-N2: FNEK sig (ECRYPTFS_SIG_SIZE)
+ * Octet N2-N3: Cipher identifier (1 octet)
+ * Octets N3-N4: Block-aligned encrypted filename
+ * - Consists of a minimum number of random numbers, a \0
+ * separator, and then the filename */
+ if (data[(*packet_size)++] != ECRYPTFS_TAG_70_PACKET_TYPE) {
+ printk(KERN_WARNING "%s: Invalid packet tag [0x%.2x]; must be "
+ "tag [0x%.2x]\n", __func__,
+ data[((*packet_size) - 1)], ECRYPTFS_TAG_70_PACKET_TYPE);
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = ecryptfs_parse_packet_length(&data[(*packet_size)],
+ &s->parsed_tag_70_packet_size,
+ &s->packet_size_len);
+ if (rc) {
+ printk(KERN_WARNING "%s: Error parsing packet length; "
+ "rc = [%d]\n", __func__, rc);
+ goto out;
+ }
+ s->block_aligned_filename_size = (s->parsed_tag_70_packet_size
+ - ECRYPTFS_SIG_SIZE - 1);
+ if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
+ > max_packet_size) {
+ printk(KERN_WARNING "%s: max_packet_size is [%d]; real packet "
+ "size is [%d]\n", __func__, max_packet_size,
+ (1 + s->packet_size_len + 1
+ + s->block_aligned_filename_size));
+ rc = -EINVAL;
+ goto out;
+ }
+ (*packet_size) += s->packet_size_len;
+ ecryptfs_to_hex(s->fnek_sig_hex, &data[(*packet_size)],
+ ECRYPTFS_SIG_SIZE);
+ s->fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+ (*packet_size) += ECRYPTFS_SIG_SIZE;
+ s->cipher_code = data[(*packet_size)++];
+ rc = ecryptfs_cipher_code_to_string(s->cipher_string, s->cipher_code);
+ if (rc) {
+ printk(KERN_WARNING "%s: Cipher code [%d] is invalid\n",
+ __func__, s->cipher_code);
+ goto out;
+ }
+ rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(&s->desc.tfm,
+ &s->tfm_mutex,
+ s->cipher_string);
+ if (unlikely(rc)) {
+ printk(KERN_ERR "Internal error whilst attempting to get "
+ "tfm and mutex for cipher name [%s]; rc = [%d]\n",
+ s->cipher_string, rc);
+ goto out;
+ }
+ mutex_lock(s->tfm_mutex);
+ rc = virt_to_scatterlist(&data[(*packet_size)],
+ s->block_aligned_filename_size, &s->src_sg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
+ "convert encrypted filename memory to scatterlist; "
+ "expected rc = 1; got rc = [%d]. "
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
+ s->block_aligned_filename_size);
+ goto out_unlock;
+ }
+ (*packet_size) += s->block_aligned_filename_size;
+ s->decrypted_filename = kmalloc(s->block_aligned_filename_size,
+ GFP_KERNEL);
+ if (!s->decrypted_filename) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting to "
+ "kmalloc [%d] bytes\n", __func__,
+ s->block_aligned_filename_size);
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+ rc = virt_to_scatterlist(s->decrypted_filename,
+ s->block_aligned_filename_size, &s->dst_sg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
+ "convert decrypted filename memory to scatterlist; "
+ "expected rc = 1; got rc = [%d]. "
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
+ s->block_aligned_filename_size);
+ goto out_free_unlock;
+ }
+ /* The characters in the first block effectively do the job of
+ * the IV here, so we just use 0's for the IV. Note the
+ * constraint that ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES
+ * >= ECRYPTFS_MAX_IV_BYTES. */
+ memset(s->iv, 0, ECRYPTFS_MAX_IV_BYTES);
+ s->desc.info = s->iv;
+ rc = ecryptfs_find_auth_tok_for_sig(&s->auth_tok, mount_crypt_stat,
+ s->fnek_sig_hex);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to find auth tok for "
+ "fnek sig [%s]; rc = [%d]\n", __func__, s->fnek_sig_hex,
+ rc);
+ goto out_free_unlock;
+ }
+ /* TODO: Support other key modules than passphrase for
+ * filename encryption */
+ BUG_ON(s->auth_tok->token_type != ECRYPTFS_PASSWORD);
+ rc = crypto_blkcipher_setkey(
+ s->desc.tfm,
+ s->auth_tok->token.password.session_key_encryption_key,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: Error setting key for crypto context; "
+ "rc = [%d]. s->auth_tok->token.password.session_key_"
+ "encryption_key = [0x%p]; mount_crypt_stat->"
+ "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
+ rc,
+ s->auth_tok->token.password.session_key_encryption_key,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ goto out_free_unlock;
+ }
+ rc = crypto_blkcipher_decrypt_iv(&s->desc, &s->dst_sg, &s->src_sg,
+ s->block_aligned_filename_size);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to decrypt filename; "
+ "rc = [%d]\n", __func__, rc);
+ goto out_free_unlock;
+ }
+ s->i = 0;
+ while (s->decrypted_filename[s->i] != '\0'
+ && s->i < s->block_aligned_filename_size)
+ s->i++;
+ if (s->i == s->block_aligned_filename_size) {
+ printk(KERN_WARNING "%s: Invalid tag 70 packet; could not "
+ "find valid separator between random characters and "
+ "the filename\n", __func__);
+ rc = -EINVAL;
+ goto out_free_unlock;
+ }
+ s->i++;
+ (*filename_size) = (s->block_aligned_filename_size - s->i);
+ if (!((*filename_size) > 0 && (*filename_size < PATH_MAX))) {
+ printk(KERN_WARNING "%s: Filename size is [%Zd], which is "
+ "invalid\n", __func__, (*filename_size));
+ rc = -EINVAL;
+ goto out_free_unlock;
+ }
+ (*filename) = kmalloc(((*filename_size) + 1), GFP_KERNEL);
+ if (!(*filename)) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting to "
+ "kmalloc [%d] bytes\n", __func__,
+ ((*filename_size) + 1));
+ rc = -ENOMEM;
+ goto out_free_unlock;
+ }
+ memcpy((*filename), &s->decrypted_filename[s->i], (*filename_size));
+ (*filename)[(*filename_size)] = '\0';
+out_free_unlock:
+ kfree(s->decrypted_filename);
+out_unlock:
+ mutex_unlock(s->tfm_mutex);
+out:
+ if (rc) {
+ (*packet_size) = 0;
+ (*filename_size) = 0;
+ (*filename) = NULL;
+ }
+ kfree(s);
+ return rc;
+}
+
+static int
ecryptfs_get_auth_tok_sig(char **sig, struct ecryptfs_auth_tok *auth_tok)
{
int rc = 0;
@@ -897,30 +1460,6 @@ out:
return rc;
}

-static int
-ecryptfs_find_global_auth_tok_for_sig(
- struct ecryptfs_global_auth_tok **global_auth_tok,
- struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig)
-{
- struct ecryptfs_global_auth_tok *walker;
- int rc = 0;
-
- (*global_auth_tok) = NULL;
- mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
- list_for_each_entry(walker,
- &mount_crypt_stat->global_auth_tok_list,
- mount_crypt_stat_list) {
- if (memcmp(walker->sig, sig, ECRYPTFS_SIG_SIZE_HEX) == 0) {
- (*global_auth_tok) = walker;
- goto out;
- }
- }
- rc = -EINVAL;
-out:
- mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
- return rc;
-}
-
/**
* ecryptfs_verify_version
* @version: The version number to confirm
@@ -990,43 +1529,6 @@ out:
}

/**
- * ecryptfs_find_auth_tok_for_sig
- * @auth_tok: Set to the matching auth_tok; NULL if not found
- * @crypt_stat: inode crypt_stat crypto context
- * @sig: Sig of auth_tok to find
- *
- * For now, this function simply looks at the registered auth_tok's
- * linked off the mount_crypt_stat, so all the auth_toks that can be
- * used must be registered at mount time. This function could
- * potentially try a lot harder to find auth_tok's (e.g., by calling
- * out to ecryptfsd to dynamically retrieve an auth_tok object) so
- * that static registration of auth_tok's will no longer be necessary.
- *
- * Returns zero on no error; non-zero on error
- */
-static int
-ecryptfs_find_auth_tok_for_sig(
- struct ecryptfs_auth_tok **auth_tok,
- struct ecryptfs_crypt_stat *crypt_stat, char *sig)
-{
- struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
- crypt_stat->mount_crypt_stat;
- struct ecryptfs_global_auth_tok *global_auth_tok;
- int rc = 0;
-
- (*auth_tok) = NULL;
- if (ecryptfs_find_global_auth_tok_for_sig(&global_auth_tok,
- mount_crypt_stat, sig)) {
- struct key *auth_tok_key;
-
- rc = ecryptfs_keyring_auth_tok_for_sig(&auth_tok_key, auth_tok,
- sig);
- } else
- (*auth_tok) = global_auth_tok->global_auth_tok;
- return rc;
-}
-
-/**
* decrypt_passphrase_encrypted_session_key - Decrypt the session key with the given auth_tok.
* @auth_tok: The passphrase authentication token to use to encrypt the FEK
* @crypt_stat: The cryptographic context
@@ -1259,7 +1761,8 @@ find_next_matching_auth_tok:
rc = -EINVAL;
goto out_wipe_list;
}
- ecryptfs_find_auth_tok_for_sig(&matching_auth_tok, crypt_stat,
+ ecryptfs_find_auth_tok_for_sig(&matching_auth_tok,
+ crypt_stat->mount_crypt_stat,
candidate_auth_tok_sig);
if (matching_auth_tok) {
found_auth_tok = 1;
@@ -1339,7 +1842,9 @@ pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
int rc;

rc = write_tag_66_packet(auth_tok->token.private_key.signature,
- ecryptfs_code_for_cipher_string(crypt_stat),
+ ecryptfs_code_for_cipher_string(
+ crypt_stat->cipher,
+ crypt_stat->key_size),
crypt_stat, &payload, &payload_len);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n");
@@ -1699,7 +2204,8 @@ encrypted_session_key_set:
dest[(*packet_size)++] = 0x04; /* version 4 */
/* TODO: Break from RFC2440 so that arbitrary ciphers can be
* specified with strings */
- cipher_code = ecryptfs_code_for_cipher_string(crypt_stat);
+ cipher_code = ecryptfs_code_for_cipher_string(crypt_stat->cipher,
+ crypt_stat->key_size);
if (cipher_code == 0) {
ecryptfs_printk(KERN_WARNING, "Unable to generate code for "
"cipher [%s]\n", crypt_stat->cipher);
--
1.5.3.6

2008-11-04 21:40:28

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 2/5] eCryptfs: Filename Encryption: Header updates

Extensions to the header file to support filename encryption.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 4 +-
fs/ecryptfs/ecryptfs_kernel.h | 61 +++++++++++++++++++++++++++++++++--------
2 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 4857327..c9839df 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -175,8 +175,8 @@ out:
*
* Returns zero on success; non-zero on error.
*/
-static int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
- loff_t offset)
+int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
+ loff_t offset)
{
int rc = 0;
char dst[MD5_DIGEST_SIZE];
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 76a95bd..b648175 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -51,12 +51,16 @@
#define ECRYPTFS_VERSIONING_XATTR 0x00000010
#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
+#define ECRYPTFS_VERSIONING_HMAC 0x00000080
+#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
+#define ECRYPTFS_VERSIONING_GCM 0x00000200
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
| ECRYPTFS_VERSIONING_PUBKEY \
| ECRYPTFS_VERSIONING_XATTR \
| ECRYPTFS_VERSIONING_MULTKEY \
- | ECRYPTFS_VERSIONING_DEVMISC)
+ | ECRYPTFS_VERSIONING_DEVMISC \
+ | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
#define ECRYPTFS_SALT_SIZE 8
@@ -232,23 +236,39 @@ struct ecryptfs_key_sig {
char keysig[ECRYPTFS_SIG_SIZE_HEX];
};

+struct ecryptfs_filename {
+ struct list_head crypt_stat_list;
+#define ECRYPTFS_FILENAME_CONTAINS_DECRYPTED 0x00000001
+ u32 flags;
+ u32 seq_no;
+ char *filename;
+ char *encrypted_filename;
+ size_t filename_size;
+ size_t encrypted_filename_size;
+ char fnek_sig[ECRYPTFS_SIG_SIZE_HEX];
+ char dentry_name[ECRYPTFS_ENCRYPTED_DENTRY_NAME_LEN + 1];
+};
+
/**
* This is the primary struct associated with each encrypted file.
*
* TODO: cache align/pack?
*/
struct ecryptfs_crypt_stat {
-#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001
-#define ECRYPTFS_POLICY_APPLIED 0x00000002
-#define ECRYPTFS_NEW_FILE 0x00000004
-#define ECRYPTFS_ENCRYPTED 0x00000008
-#define ECRYPTFS_SECURITY_WARNING 0x00000010
-#define ECRYPTFS_ENABLE_HMAC 0x00000020
-#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040
-#define ECRYPTFS_KEY_VALID 0x00000080
-#define ECRYPTFS_METADATA_IN_XATTR 0x00000100
-#define ECRYPTFS_VIEW_AS_ENCRYPTED 0x00000200
-#define ECRYPTFS_KEY_SET 0x00000400
+#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001
+#define ECRYPTFS_POLICY_APPLIED 0x00000002
+#define ECRYPTFS_NEW_FILE 0x00000004
+#define ECRYPTFS_ENCRYPTED 0x00000008
+#define ECRYPTFS_SECURITY_WARNING 0x00000010
+#define ECRYPTFS_ENABLE_HMAC 0x00000020
+#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040
+#define ECRYPTFS_KEY_VALID 0x00000080
+#define ECRYPTFS_METADATA_IN_XATTR 0x00000100
+#define ECRYPTFS_VIEW_AS_ENCRYPTED 0x00000200
+#define ECRYPTFS_KEY_SET 0x00000400
+#define ECRYPTFS_ENCRYPT_FILENAMES 0x00000800
+#define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
+#define ECRYPTFS_ENCFN_USE_FEK 0x00002000
u32 flags;
unsigned int file_version;
size_t iv_bytes;
@@ -597,6 +617,15 @@ struct ecryptfs_open_req {
int ecryptfs_interpose(struct dentry *hidden_dentry,
struct dentry *this_dentry, struct super_block *sb,
u32 flags);
+int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
+ struct dentry *lower_dentry,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct inode *ecryptfs_dir_inode,
+ struct nameidata *ecryptfs_nd);
+int ecryptfs_decode_and_decrypt_filename(char **decrypted_name,
+ size_t *decrypted_name_size,
+ struct dentry *ecryptfs_dentry,
+ const char *name, size_t name_size);
int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
const char *name, int length,
@@ -604,6 +633,12 @@ int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
const char *name, int length,
char **encoded_name);
+int ecryptfs_encrypt_and_encode_filename(
+ char **encoded_name,
+ size_t *encoded_name_size,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ const char *name, size_t name_size);
struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
void ecryptfs_dump_hex(char *data, int bytes);
int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
@@ -730,5 +765,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
size_t *packet_size,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
char *data, size_t max_packet_size);
+int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
+ loff_t offset);

#endif /* #ifndef ECRYPTFS_KERNEL_H */
--
1.5.3.6

2008-11-04 21:41:46

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 3/5] eCryptfs: Filename Encryption: Encoding and encryption functions

These functions support encrypting and encoding the filename
contents. The encrypted filename contents may consist of any ASCII
characters. This patch includes a custom encoding mechanism to map the
ASCII characters to a reduced character set that is appropriate for
filenames.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 433 insertions(+), 0 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index c9839df..18c78ab 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1721,6 +1721,98 @@ out:
}

/**
+ * ecryptfs_encrypt_filename - encrypt filename
+ *
+ * CBC-encrypts the filename. We do not want to encrypt the same
+ * filename with the same key and IV, which may happen with hard
+ * links, so we prepend random bits to each filename.
+ *
+ * Returns zero on success; non-zero otherwise
+ */
+static int
+ecryptfs_encrypt_filename(struct ecryptfs_filename *filename,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
+{
+ int rc = 0;
+
+ filename->encrypted_filename = NULL;
+ filename->encrypted_filename_size = 0;
+ if ((crypt_stat && (crypt_stat->flags & ECRYPTFS_ENCFN_USE_MOUNT_FNEK))
+ || (mount_crypt_stat && (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK))) {
+ size_t packet_size;
+ size_t remaining_bytes;
+
+ rc = ecryptfs_write_tag_70_packet(
+ NULL, NULL,
+ &filename->encrypted_filename_size,
+ mount_crypt_stat, NULL,
+ filename->filename_size);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to get packet "
+ "size for tag 72; rc = [%d]\n", __func__,
+ rc);
+ filename->encrypted_filename_size = 0;
+ goto out;
+ }
+ filename->encrypted_filename =
+ kmalloc(filename->encrypted_filename_size, GFP_KERNEL);
+ if (!filename->encrypted_filename) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting "
+ "to kmalloc [%Zd] bytes\n", __func__,
+ filename->encrypted_filename_size);
+ rc = -ENOMEM;
+ goto out;
+ }
+ remaining_bytes = filename->encrypted_filename_size;
+ rc = ecryptfs_write_tag_70_packet(filename->encrypted_filename,
+ &remaining_bytes,
+ &packet_size,
+ mount_crypt_stat,
+ filename->filename,
+ filename->filename_size);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to generate "
+ "tag 70 packet; rc = [%d]\n", __func__,
+ rc);
+ kfree(filename->encrypted_filename);
+ filename->encrypted_filename = NULL;
+ filename->encrypted_filename_size = 0;
+ goto out;
+ }
+ filename->encrypted_filename_size = packet_size;
+ } else {
+ printk(KERN_ERR "%s: No support for requested filename "
+ "encryption method in this release\n", __func__);
+ rc = -ENOTSUPP;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int ecryptfs_copy_filename(char **copied_name, size_t *copied_name_size,
+ const char *name, size_t name_size)
+{
+ int rc = 0;
+
+ (*copied_name) = kmalloc((name_size + 2), GFP_KERNEL);
+ if (!(*copied_name)) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy((void *)(*copied_name), (void *)name, name_size);
+ (*copied_name)[(name_size)] = '\0'; /* Only for convenience
+ * in printing out the
+ * string in debug
+ * messages */
+ (*copied_name_size) = (name_size + 1);
+out:
+ return rc;
+}
+
+/**
* ecryptfs_process_key_cipher - Perform key cipher initialization.
* @key_tfm: Crypto context for key material, set by this function
* @cipher_name: Name of the cipher
@@ -1911,3 +2003,344 @@ out:
mutex_unlock(&key_tfm_list_mutex);
return rc;
}
+
+/* 64 characters forming a 6-bit target field */
+static unsigned char *portable_filename_chars = ("-.0123456789ABCD"
+ "EFGHIJKLMNOPQRST"
+ "UVWXYZabcdefghij"
+ "klmnopqrstuvwxyz");
+
+/* We could either offset on every reverse map or just pad some 0x00's
+ * at the front here */
+static unsigned char filename_rev_map[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 15 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 31 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 39 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* 47 */
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, /* 55 */
+ 0x0A, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 63 */
+ 0x00, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, /* 71 */
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, /* 79 */
+ 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, /* 87 */
+ 0x23, 0x24, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, /* 95 */
+ 0x00, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, /* 103 */
+ 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, /* 111 */
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, /* 119 */
+ 0x3D, 0x3E, 0x3F
+};
+
+/**
+ * ecryptfs_encode_for_filename
+ * @dst: Destination location for encoded filename
+ * @dst_size: Size of the encoded filename in bytes
+ * @src: Source location for the filename to encode
+ * @src_size: Size of the source in bytes
+ */
+void ecryptfs_encode_for_filename(unsigned char *dst, size_t *dst_size,
+ unsigned char *src, size_t src_size)
+{
+ size_t num_blocks;
+ size_t block_num = 0;
+ size_t dst_offset = 0;
+ unsigned char last_block[3];
+
+ if (src_size == 0) {
+ (*dst_size) = 0;
+ goto out;
+ }
+ num_blocks = (src_size / 3);
+ if ((src_size % 3) == 0) {
+ memcpy(last_block, (&src[src_size - 3]), 3);
+ } else {
+ num_blocks++;
+ last_block[2] = 0x00;
+ switch (src_size % 3) {
+ case 1:
+ last_block[0] = src[src_size - 1];
+ last_block[1] = 0x00;
+ break;
+ case 2:
+ last_block[0] = src[src_size - 2];
+ last_block[1] = src[src_size - 1];
+ }
+ }
+ (*dst_size) = (num_blocks * 4);
+ if (!dst)
+ goto out;
+ while (block_num < num_blocks) {
+ unsigned char *src_block;
+ unsigned char dst_block[4];
+
+ if (block_num == (num_blocks - 1))
+ src_block = last_block;
+ else
+ src_block = &src[block_num * 3];
+ dst_block[0] = ((src_block[0] >> 2) & 0x3F);
+ dst_block[1] = (((src_block[0] << 4) & 0x30)
+ | ((src_block[1] >> 4) & 0x0F));
+ dst_block[2] = (((src_block[1] << 2) & 0x3C)
+ | ((src_block[2] >> 6) & 0x03));
+ dst_block[3] = (src_block[2] & 0x3F);
+ dst[dst_offset++] = portable_filename_chars[dst_block[0]];
+ dst[dst_offset++] = portable_filename_chars[dst_block[1]];
+ dst[dst_offset++] = portable_filename_chars[dst_block[2]];
+ dst[dst_offset++] = portable_filename_chars[dst_block[3]];
+ block_num++;
+ }
+out:
+ return;
+}
+
+int ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
+ const unsigned char *src, size_t src_size)
+{
+ u8 current_bit_offset = 0;
+ size_t src_byte_offset = 0;
+ size_t dst_byte_offset = 0;
+ int rc = 0;
+
+ if (dst == NULL) {
+ /* Not exact; conservatively long */
+ (*dst_size) = (((src_size + 1) * 3) / 4);
+ goto out;
+ }
+ while (src_byte_offset < src_size) {
+ unsigned char src_byte =
+ filename_rev_map[(int)src[src_byte_offset]];
+
+ switch (current_bit_offset) {
+ case 0:
+ dst[dst_byte_offset] = (src_byte << 2);
+ current_bit_offset = 6;
+ break;
+ case 6:
+ dst[dst_byte_offset++] |= (src_byte >> 4);
+ dst[dst_byte_offset] = ((src_byte & 0xF)
+ << 4);
+ current_bit_offset = 4;
+ break;
+ case 4:
+ dst[dst_byte_offset++] |= (src_byte >> 2);
+ dst[dst_byte_offset] = (src_byte << 6);
+ current_bit_offset = 2;
+ break;
+ case 2:
+ dst[dst_byte_offset++] |= (src_byte);
+ dst[dst_byte_offset] = 0;
+ current_bit_offset = 0;
+ break;
+ }
+ src_byte_offset++;
+ }
+ (*dst_size) = dst_byte_offset;
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_encrypt_and_encode_filename - converts a plaintext file name to cipher text
+ * @crypt_stat: The crypt_stat struct associated with the file anem to encode
+ * @name: The plaintext name
+ * @length: The length of the plaintext
+ * @encoded_name: The encypted name
+ *
+ * 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_stat->tfm.
+ *
+ * Returns zero on success; non-zero on otherwise
+ */
+int ecryptfs_encrypt_and_encode_filename(
+ char **encoded_name,
+ size_t *encoded_name_size,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ const char *name, size_t name_size)
+{
+ size_t encoded_name_no_prefix_size;
+ int rc = 0;
+
+ (*encoded_name) = NULL;
+ (*encoded_name_size) = 0;
+ if ((crypt_stat && (crypt_stat->flags & ECRYPTFS_ENCRYPT_FILENAMES))
+ || (mount_crypt_stat && (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES))) {
+ struct ecryptfs_filename *filename;
+
+ filename = kzalloc(sizeof(*filename), GFP_KERNEL);
+ if (!filename) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting "
+ "to kzalloc [%d] bytes\n", __func__,
+ sizeof(*filename));
+ rc = -ENOMEM;
+ goto out;
+ }
+ filename->filename = (char *)name;
+ filename->filename_size = name_size;
+ rc = ecryptfs_encrypt_filename(filename, crypt_stat,
+ mount_crypt_stat);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to encrypt "
+ "filename; rc = [%d]\n", __func__, rc);
+ kfree(filename);
+ goto out;
+ }
+ ecryptfs_encode_for_filename(
+ NULL, &encoded_name_no_prefix_size,
+ filename->encrypted_filename,
+ filename->encrypted_filename_size);
+ if ((crypt_stat && (crypt_stat->flags
+ & ECRYPTFS_ENCFN_USE_MOUNT_FNEK))
+ || (mount_crypt_stat
+ && (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK)))
+ (*encoded_name_size) =
+ (ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE
+ + encoded_name_no_prefix_size);
+ else
+ (*encoded_name_size) =
+ (ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX_SIZE
+ + encoded_name_no_prefix_size);
+ (*encoded_name) = kmalloc((*encoded_name_size) + 1, GFP_KERNEL);
+ if (!(*encoded_name)) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting "
+ "to kzalloc [%d] bytes\n", __func__,
+ (*encoded_name_size));
+ rc = -ENOMEM;
+ kfree(filename->encrypted_filename);
+ kfree(filename);
+ goto out;
+ }
+ if ((crypt_stat && (crypt_stat->flags
+ & ECRYPTFS_ENCFN_USE_MOUNT_FNEK))
+ || (mount_crypt_stat
+ && (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK))) {
+ memcpy((*encoded_name),
+ ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX,
+ ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE);
+ ecryptfs_encode_for_filename(
+ ((*encoded_name)
+ + ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE),
+ &encoded_name_no_prefix_size,
+ filename->encrypted_filename,
+ filename->encrypted_filename_size);
+ (*encoded_name_size) =
+ (ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE
+ + encoded_name_no_prefix_size);
+ (*encoded_name)[(*encoded_name_size)] = '\0';
+ (*encoded_name_size)++;
+ } else {
+ rc = -ENOTSUPP;
+ }
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to encode "
+ "encrypted filename; rc = [%d]\n", __func__,
+ rc);
+ kfree((*encoded_name));
+ (*encoded_name) = NULL;
+ (*encoded_name_size) = 0;
+ }
+ kfree(filename->encrypted_filename);
+ kfree(filename);
+ } else {
+ rc = ecryptfs_copy_filename(encoded_name,
+ encoded_name_size,
+ name, name_size);
+ }
+out:
+ return rc;
+}
+
+/**
+ * ecryptfs_decode_and_decrypt_filename - converts the encoded cipher text name to decoded plaintext
+ * @plaintext_name: The plaintext name
+ * @plaintext_name_size: The plaintext name size
+ * @ecryptfs_dir_dentry: eCryptfs directory dentry
+ * @name: The filename in cipher text
+ * @name_size: The cipher text name size
+ *
+ * Decrypts and decodes the filename.
+ *
+ * Returns zero on error; non-zero otherwise
+ */
+int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
+ size_t *plaintext_name_size,
+ struct dentry *ecryptfs_dir_dentry,
+ const char *name, size_t name_size)
+{
+ char *decoded_name;
+ size_t decoded_name_size;
+ size_t packet_size;
+ int rc = 0;
+
+ if ((name_size > ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE)
+ && (strncmp(name, ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX,
+ ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE) == 0)) {
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &ecryptfs_superblock_to_private(
+ ecryptfs_dir_dentry->d_sb)->mount_crypt_stat;
+ const char *orig_name = name;
+ size_t orig_name_size = name_size;
+
+ name += ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE;
+ name_size -= ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE;
+ rc = ecryptfs_decode_from_filename(NULL, &decoded_name_size,
+ name, name_size);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to decode "
+ "filename; rc = [%d]\n", __func__, rc);
+ rc = ecryptfs_copy_filename(plaintext_name,
+ plaintext_name_size,
+ orig_name, orig_name_size);
+ goto out;
+ }
+ decoded_name = kmalloc(decoded_name_size, GFP_KERNEL);
+ if (!decoded_name) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting "
+ "to kmalloc [%Zd] bytes\n", __func__,
+ decoded_name_size);
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = ecryptfs_decode_from_filename(decoded_name,
+ &decoded_name_size,
+ name, name_size);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to decode "
+ "filename; rc = [%d]\n", __func__, rc);
+ rc = ecryptfs_copy_filename(plaintext_name,
+ plaintext_name_size,
+ orig_name, orig_name_size);
+ goto out_free;
+ }
+ rc = ecryptfs_parse_tag_70_packet(plaintext_name,
+ plaintext_name_size,
+ &packet_size,
+ mount_crypt_stat,
+ decoded_name,
+ decoded_name_size);
+ if (rc) {
+ printk(KERN_INFO "%s: Could not parse tag 70 packet "
+ "from filename; copying through filename "
+ "as-is\n", __func__);
+ rc = ecryptfs_copy_filename(plaintext_name,
+ plaintext_name_size,
+ orig_name, orig_name_size);
+ goto out_free;
+ }
+ } else {
+ rc = ecryptfs_copy_filename(plaintext_name,
+ plaintext_name_size,
+ name, name_size);
+ goto out;
+ }
+out_free:
+ kfree(decoded_name);
+out:
+ return rc;
+}
--
1.5.3.6

2008-11-04 21:42:40

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 4/5] eCryptfs: Filename Encryption: filldir, lookup, and readlink

Make the requisite modifications to ecryptfs_filldir(), ecryptfs_lookup(),
and ecryptfs_readlink() to call out to filename encryption functions.
Propagate filename encryption policy flags from mount-wide crypt_stat
to inode crypt_stat.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 107 ++-------------
fs/ecryptfs/ecryptfs_kernel.h | 6 -
fs/ecryptfs/file.c | 30 ++--
fs/ecryptfs/inode.c | 294 +++++++++++++++++++++++------------------
4 files changed, 195 insertions(+), 242 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 18c78ab..ea2afd2 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -924,6 +924,15 @@ static void ecryptfs_copy_mount_wide_flags_to_inode_flags(
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)
crypt_stat->flags |= ECRYPTFS_VIEW_AS_ENCRYPTED;
+ if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
+ crypt_stat->flags |= ECRYPTFS_ENCRYPT_FILENAMES;
+ if (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK)
+ crypt_stat->flags |= ECRYPTFS_ENCFN_USE_MOUNT_FNEK;
+ else if (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCFN_USE_FEK)
+ crypt_stat->flags |= ECRYPTFS_ENCFN_USE_FEK;
+ }
}

static int ecryptfs_copy_mount_wide_sigs_to_inode_sigs(
@@ -1060,7 +1069,8 @@ struct ecryptfs_flag_map_elem {
static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
{0x00000001, ECRYPTFS_ENABLE_HMAC},
{0x00000002, ECRYPTFS_ENCRYPTED},
- {0x00000004, ECRYPTFS_METADATA_IN_XATTR}
+ {0x00000004, ECRYPTFS_METADATA_IN_XATTR},
+ {0x00000008, ECRYPTFS_ENCRYPT_FILENAMES}
};

/**
@@ -1213,6 +1223,8 @@ int ecryptfs_read_and_validate_header_region(char *data,
&(ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat);
int rc;

+ if (crypt_stat->extent_size == 0)
+ crypt_stat->extent_size = ECRYPTFS_DEFAULT_EXTENT_SIZE;
rc = ecryptfs_read_lower(data, 0, crypt_stat->extent_size,
ecryptfs_inode);
if (rc) {
@@ -1222,7 +1234,6 @@ int ecryptfs_read_and_validate_header_region(char *data,
}
if (!contains_ecryptfs_marker(data + ECRYPTFS_FILE_SIZE_BYTES)) {
rc = -EINVAL;
- ecryptfs_printk(KERN_DEBUG, "Valid marker not found\n");
}
out:
return rc;
@@ -1629,98 +1640,6 @@ out:
}

/**
- * ecryptfs_encode_filename - converts a plaintext file name to cipher text
- * @crypt_stat: The crypt_stat struct associated with the file anem to encode
- * @name: The plaintext name
- * @length: The length of the plaintext
- * @encoded_name: The encypted name
- *
- * 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_stat->tfm.
- *
- * TODO: Implement filename decoding and decryption here, in place of
- * memcpy. We are keeping the framework around for now to (1)
- * facilitate testing of the components needed to implement filename
- * encryption and (2) to provide a code base from which other
- * developers in the community can easily implement this feature.
- *
- * Returns the length of encoded filename; negative if error
- */
-int
-ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
- const char *name, int length, char **encoded_name)
-{
- int error = 0;
-
- (*encoded_name) = kmalloc(length + 2, GFP_KERNEL);
- if (!(*encoded_name)) {
- error = -ENOMEM;
- goto out;
- }
- /* TODO: Filename encryption is a scheduled feature for a
- * future version of eCryptfs. This function is here only for
- * the purpose of providing a framework for other developers
- * to easily implement filename encryption. Hint: Replace this
- * memcpy() with a call to encrypt and encode the
- * filename, the set the length accordingly. */
- memcpy((void *)(*encoded_name), (void *)name, length);
- (*encoded_name)[length] = '\0';
- error = length + 1;
-out:
- return error;
-}
-
-/**
- * ecryptfs_decode_filename - converts the cipher text name to plaintext
- * @crypt_stat: The crypt_stat struct associated with the file
- * @name: The filename in cipher text
- * @length: The length of the cipher text name
- * @decrypted_name: The plaintext name
- *
- * Decodes and decrypts the filename.
- *
- * We assume that we have a properly initialized crypto context,
- * pointed to by crypt_stat->tfm.
- *
- * TODO: Implement filename decoding and decryption here, in place of
- * memcpy. We are keeping the framework around for now to (1)
- * facilitate testing of the components needed to implement filename
- * encryption and (2) to provide a code base from which other
- * developers in the community can easily implement this feature.
- *
- * Returns the length of decoded filename; negative if error
- */
-int
-ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
- const char *name, int length, char **decrypted_name)
-{
- int error = 0;
-
- (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL);
- if (!(*decrypted_name)) {
- error = -ENOMEM;
- goto out;
- }
- /* TODO: Filename encryption is a scheduled feature for a
- * future version of eCryptfs. This function is here only for
- * the purpose of providing a framework for other developers
- * to easily implement filename encryption. Hint: Replace this
- * memcpy() with a call to decode and decrypt the
- * filename, the set the length accordingly. */
- 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:
- return error;
-}
-
-/**
* ecryptfs_encrypt_filename - encrypt filename
*
* CBC-encrypts the filename. We do not want to encrypt the same
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index b648175..c11fc95 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -627,12 +627,6 @@ int ecryptfs_decode_and_decrypt_filename(char **decrypted_name,
struct dentry *ecryptfs_dentry,
const char *name, size_t name_size);
int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
-int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
- const char *name, int length,
- char **decrypted_name);
-int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
- const char *name, int length,
- char **encoded_name);
int ecryptfs_encrypt_and_encode_filename(
char **encoded_name,
size_t *encoded_name_size,
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index eb3dc4c..6b0a99d 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -77,27 +77,27 @@ struct ecryptfs_getdents_callback {

/* Inspired by generic filldir in fs/readdir.c */
static int
-ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset,
- u64 ino, unsigned int d_type)
+ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
{
- struct ecryptfs_crypt_stat *crypt_stat;
struct ecryptfs_getdents_callback *buf =
(struct ecryptfs_getdents_callback *)dirent;
+ int name_size;
+ char *name;
int rc;
- int decoded_length;
- char *decoded_name;

- crypt_stat = ecryptfs_dentry_to_private(buf->dentry)->crypt_stat;
buf->filldir_called++;
- decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen,
- &decoded_name);
- if (decoded_length < 0) {
- rc = decoded_length;
+ rc = ecryptfs_decode_and_decrypt_filename(&name, &name_size,
+ buf->dentry, lower_name,
+ lower_namelen);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to decode and decrypt "
+ "filename [%s]; rc = [%d]\n", __func__, lower_name,
+ rc);
goto out;
}
- rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
- ino, d_type);
- kfree(decoded_name);
+ rc = buf->filldir(buf->dirent, name, name_size, offset, ino, d_type);
+ kfree(name);
if (rc >= 0)
buf->entries_written++;
out:
@@ -106,8 +106,8 @@ out:

/**
* ecryptfs_readdir
- * @file: The ecryptfs file struct
- * @dirent: Directory entry
+ * @file: The eCryptfs directory file
+ * @dirent: Directory entry handle
* @filldir: The filldir callback function
*/
static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 89209f0..cf50729 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -228,8 +228,7 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
{
int rc;

- /* ecryptfs_do_create() calls ecryptfs_interpose(), which opens
- * the crypt_stat->lower_file (persistent file) */
+ /* ecryptfs_do_create() calls ecryptfs_interpose() */
rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd);
if (unlikely(rc)) {
ecryptfs_printk(KERN_WARNING, "Failed to create file in"
@@ -244,141 +243,91 @@ out:
}

/**
- * ecryptfs_lookup
- * @dir: inode
- * @dentry: The dentry
- * @nd: nameidata, may be NULL
- *
- * 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.
+ * ecryptfs_lookup_and_interpose_lower - Perform a lookup
*/
-static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
- struct nameidata *nd)
+int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
+ struct dentry *lower_dentry,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct inode *ecryptfs_dir_inode,
+ struct nameidata *ecryptfs_nd)
{
- int rc = 0;
struct dentry *lower_dir_dentry;
- struct dentry *lower_dentry;
struct vfsmount *lower_mnt;
- char *encoded_name;
- int encoded_namelen;
- struct ecryptfs_crypt_stat *crypt_stat = NULL;
+ struct inode *lower_inode;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
char *page_virt = NULL;
- struct inode *lower_inode;
u64 file_size;
+ int rc = 0;

- lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
- dentry->d_op = &ecryptfs_dops;
- if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, "."))
- || (dentry->d_name.len == 2
- && !strcmp(dentry->d_name.name, ".."))) {
- d_drop(dentry);
- goto out;
- }
- encoded_namelen = ecryptfs_encode_filename(crypt_stat,
- dentry->d_name.name,
- dentry->d_name.len,
- &encoded_name);
- if (encoded_namelen < 0) {
- rc = encoded_namelen;
- d_drop(dentry);
- goto out;
- }
- ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen "
- "= [%d]\n", encoded_name, encoded_namelen);
- lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry,
- encoded_namelen - 1);
- kfree(encoded_name);
- if (IS_ERR(lower_dentry)) {
- ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n");
- rc = PTR_ERR(lower_dentry);
- d_drop(dentry);
- goto out;
- }
- lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
- ecryptfs_printk(KERN_DEBUG, "lower_dentry = [%p]; lower_dentry->"
- "d_name.name = [%s]\n", lower_dentry,
- lower_dentry->d_name.name);
+ lower_dir_dentry = lower_dentry->d_parent;
+ lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(
+ ecryptfs_dentry->d_parent));
lower_inode = lower_dentry->d_inode;
- fsstack_copy_attr_atime(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode);
BUG_ON(!atomic_read(&lower_dentry->d_count));
- ecryptfs_set_dentry_private(dentry,
+ ecryptfs_set_dentry_private(ecryptfs_dentry,
kmem_cache_alloc(ecryptfs_dentry_info_cache,
GFP_KERNEL));
- if (!ecryptfs_dentry_to_private(dentry)) {
+ if (!ecryptfs_dentry_to_private(ecryptfs_dentry)) {
rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting "
- "to allocate ecryptfs_dentry_info struct\n");
+ printk(KERN_ERR "%s: Out of memory whilst attempting "
+ "to allocate ecryptfs_dentry_info struct\n",
+ __func__);
goto out_dput;
}
- ecryptfs_set_dentry_lower(dentry, lower_dentry);
- ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt);
+ ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry);
+ ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt);
if (!lower_dentry->d_inode) {
/* We want to add because we couldn't find in lower */
- d_add(dentry, NULL);
+ d_add(ecryptfs_dentry, NULL);
goto out;
}
- rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb,
- ECRYPTFS_INTERPOSE_FLAG_D_ADD);
+ rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry,
+ ecryptfs_dir_inode->i_sb, 1);
if (rc) {
- ecryptfs_printk(KERN_ERR, "Error interposing\n");
+ printk(KERN_ERR "%s: Error interposing; rc = [%d]\n",
+ __func__, rc);
goto out;
}
- if (S_ISDIR(lower_inode->i_mode)) {
- ecryptfs_printk(KERN_DEBUG, "Is a directory; returning\n");
+ if (S_ISDIR(lower_inode->i_mode))
goto out;
- }
- if (S_ISLNK(lower_inode->i_mode)) {
- ecryptfs_printk(KERN_DEBUG, "Is a symlink; returning\n");
+ if (S_ISLNK(lower_inode->i_mode))
goto out;
- }
- if (special_file(lower_inode->i_mode)) {
- ecryptfs_printk(KERN_DEBUG, "Is a special file; returning\n");
+ if (special_file(lower_inode->i_mode))
goto out;
- }
- if (!nd) {
- ecryptfs_printk(KERN_DEBUG, "We have a NULL nd, just leave"
- "as we *think* we are about to unlink\n");
+ if (!ecryptfs_nd)
goto out;
- }
/* Released in this function */
- page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2,
- GFP_USER);
+ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, GFP_USER);
if (!page_virt) {
+ printk(KERN_ERR "%s: Cannot kmem_cache_zalloc() a page\n",
+ __func__);
rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR,
- "Cannot ecryptfs_kmalloc a page\n");
goto out;
}
- crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
- if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED))
- ecryptfs_set_default_sizes(crypt_stat);
- if (!ecryptfs_inode_to_private(dentry->d_inode)->lower_file) {
- rc = ecryptfs_init_persistent_file(dentry);
+ if (!ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->lower_file) {
+ rc = ecryptfs_init_persistent_file(ecryptfs_dentry);
if (rc) {
printk(KERN_ERR "%s: Error attempting to initialize "
"the persistent file for the dentry with name "
"[%s]; rc = [%d]\n", __func__,
- dentry->d_name.name, rc);
- goto out;
+ ecryptfs_dentry->d_name.name, rc);
+ goto out_free_kmem;
}
}
rc = ecryptfs_read_and_validate_header_region(page_virt,
- dentry->d_inode);
+ ecryptfs_dentry->d_inode);
if (rc) {
- rc = ecryptfs_read_and_validate_xattr_region(page_virt, dentry);
+ rc = ecryptfs_read_and_validate_xattr_region(page_virt,
+ ecryptfs_dentry);
if (rc) {
- printk(KERN_DEBUG "Valid metadata not found in header "
- "region or xattr region; treating file as "
- "unencrypted\n");
rc = 0;
- kmem_cache_free(ecryptfs_header_cache_2, page_virt);
- goto out;
+ goto out_free_kmem;
}
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
}
mount_crypt_stat = &ecryptfs_superblock_to_private(
- dentry->d_sb)->mount_crypt_stat;
+ ecryptfs_dentry->d_sb)->mount_crypt_stat;
if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) {
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
file_size = (crypt_stat->num_header_bytes_at_front
@@ -388,14 +337,103 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
} else {
file_size = get_unaligned_be64(page_virt);
}
- i_size_write(dentry->d_inode, (loff_t)file_size);
+ i_size_write(ecryptfs_dentry->d_inode, (loff_t)file_size);
+out_free_kmem:
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;
-
out_dput:
dput(lower_dentry);
- d_drop(dentry);
+ d_drop(ecryptfs_dentry);
out:
+ return rc;
+}
+
+/**
+ * ecryptfs_lookup
+ * @ecryptfs_dir_inode: The eCryptfs directory inode
+ * @ecryptfs_dentry: The eCryptfs dentry that we are looking up
+ * @ecryptfs_nd: nameidata; may be NULL
+ *
+ * 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.
+ */
+static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
+ struct dentry *ecryptfs_dentry,
+ struct nameidata *ecryptfs_nd)
+{
+ char *encrypted_and_encoded_name = NULL;
+ int encrypted_and_encoded_name_size;
+ struct ecryptfs_crypt_stat *crypt_stat = NULL;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
+ struct ecryptfs_inode_info *inode_info;
+ struct dentry *lower_dir_dentry, *lower_dentry;
+ int rc = 0;
+
+ ecryptfs_dentry->d_op = &ecryptfs_dops;
+ if ((ecryptfs_dentry->d_name.len == 1
+ && !strcmp(ecryptfs_dentry->d_name.name, "."))
+ || (ecryptfs_dentry->d_name.len == 2
+ && !strcmp(ecryptfs_dentry->d_name.name, ".."))) {
+ goto out_d_drop;
+ }
+ lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent);
+ lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name,
+ lower_dir_dentry,
+ ecryptfs_dentry->d_name.len);
+ if (IS_ERR(lower_dentry)) {
+ rc = PTR_ERR(lower_dentry);
+ printk(KERN_ERR "%s: lookup_one_len() returned [%d] on "
+ "lower_dentry = [%s]\n", __func__, rc,
+ ecryptfs_dentry->d_name.name);
+ goto out_d_drop;
+ }
+ if (lower_dentry->d_inode)
+ goto lookup_and_interpose;
+ inode_info = ecryptfs_inode_to_private(ecryptfs_dentry->d_inode);
+ if (inode_info) {
+ crypt_stat = &inode_info->crypt_stat;
+ /* TODO: lock for crypt_stat comparison */
+ if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED))
+ ecryptfs_set_default_sizes(crypt_stat);
+ }
+ if (crypt_stat)
+ mount_crypt_stat = crypt_stat->mount_crypt_stat;
+ else
+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat;
+ if (!(crypt_stat && (crypt_stat->flags & ECRYPTFS_ENCRYPT_FILENAMES))
+ && !(mount_crypt_stat && (mount_crypt_stat->flags
+ & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)))
+ goto lookup_and_interpose;
+ dput(lower_dentry);
+ rc = ecryptfs_encrypt_and_encode_filename(
+ &encrypted_and_encoded_name, &encrypted_and_encoded_name_size,
+ crypt_stat, mount_crypt_stat, ecryptfs_dentry->d_name.name,
+ ecryptfs_dentry->d_name.len);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to encrypt and encode "
+ "filename; rc = [%d]\n", __func__, rc);
+ goto out_d_drop;
+ }
+ lower_dentry = lookup_one_len(encrypted_and_encoded_name,
+ lower_dir_dentry,
+ encrypted_and_encoded_name_size - 1);
+ if (IS_ERR(lower_dentry)) {
+ rc = PTR_ERR(lower_dentry);
+ printk(KERN_ERR "%s: lookup_one_len() returned [%d] on "
+ "lower_dentry = [%s]\n", __func__, rc,
+ encrypted_and_encoded_name);
+ goto out_d_drop;
+ }
+lookup_and_interpose:
+ rc = ecryptfs_lookup_and_interpose_lower(ecryptfs_dentry, lower_dentry,
+ crypt_stat, ecryptfs_dir_inode,
+ ecryptfs_nd);
+ goto out;
+out_d_drop:
+ d_drop(ecryptfs_dentry);
+out:
+ kfree(encrypted_and_encoded_name);
return ERR_PTR(rc);
}

@@ -466,19 +504,21 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
char *encoded_symname;
- int encoded_symlen;
- struct ecryptfs_crypt_stat *crypt_stat = NULL;
+ size_t encoded_symlen;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;

lower_dentry = ecryptfs_dentry_to_lower(dentry);
dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
- strlen(symname),
- &encoded_symname);
- if (encoded_symlen < 0) {
- rc = encoded_symlen;
+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ dir->i_sb)->mount_crypt_stat;
+ rc = ecryptfs_encrypt_and_encode_filename(&encoded_symname,
+ &encoded_symlen,
+ NULL,
+ mount_crypt_stat, symname,
+ strlen(symname));
+ if (rc)
goto out_lock;
- }
rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
encoded_symname);
kfree(encoded_symname);
@@ -602,14 +642,15 @@ out_lock:
}

static int
-ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
+ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
{
- int rc;
- struct dentry *lower_dentry;
- char *decoded_name;
char *lower_buf;
- mm_segment_t old_fs;
+ struct dentry *lower_dentry;
struct ecryptfs_crypt_stat *crypt_stat;
+ char *plaintext_name;
+ size_t plaintext_name_size;
+ mm_segment_t old_fs;
+ int rc;

lower_dentry = ecryptfs_dentry_to_lower(dentry);
if (!lower_dentry->d_inode->i_op ||
@@ -617,38 +658,39 @@ ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
rc = -EINVAL;
goto out;
}
+ crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
/* Released in this function */
lower_buf = kmalloc(bufsiz, GFP_KERNEL);
if (lower_buf == NULL) {
- ecryptfs_printk(KERN_ERR, "Out of memory\n");
+ printk(KERN_ERR "%s: Out of memory whilst attempting to "
+ "kmalloc [%d] bytes\n", __func__, bufsiz);
rc = -ENOMEM;
goto out;
}
old_fs = get_fs();
set_fs(get_ds());
- ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ "
- "lower_dentry->d_name.name = [%s]\n",
- lower_dentry->d_name.name);
rc = lower_dentry->d_inode->i_op->readlink(lower_dentry,
(char __user *)lower_buf,
bufsiz);
set_fs(old_fs);
if (rc >= 0) {
- crypt_stat = NULL;
- rc = ecryptfs_decode_filename(crypt_stat, lower_buf, rc,
- &decoded_name);
- if (rc == -ENOMEM)
+ rc = ecryptfs_decode_and_decrypt_filename(&plaintext_name,
+ &plaintext_name_size,
+ dentry, lower_buf,
+ rc);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to decode and "
+ "decrypt filename; rc = [%d]\n", __func__,
+ rc);
goto out_free_lower_buf;
- if (rc > 0) {
- ecryptfs_printk(KERN_DEBUG, "Copying [%d] bytes "
- "to userspace: [%*s]\n", rc,
- decoded_name);
- if (copy_to_user(buf, decoded_name, rc))
- rc = -EFAULT;
}
- kfree(decoded_name);
- fsstack_copy_attr_atime(dentry->d_inode,
- lower_dentry->d_inode);
+ rc = copy_to_user(buf, plaintext_name, plaintext_name_size);
+ if (rc)
+ rc = -EFAULT;
+ else
+ rc = plaintext_name_size;
+ kfree(plaintext_name);
+ fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode);
}
out_free_lower_buf:
kfree(lower_buf);
@@ -670,8 +712,6 @@ static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
}
old_fs = get_fs();
set_fs(get_ds());
- ecryptfs_printk(KERN_DEBUG, "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);
--
1.5.3.6

2008-11-04 21:43:28

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH 5/5] eCryptfs: Filename Encryption: mount option

Enable mount-wide filename encryption by providing the Filename
Encryption Key (FNEK) signature as a mount option. Note that the
ecryptfs-utils userspace package versions 61 or later support
this option.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/main.c | 126 ++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 99 insertions(+), 27 deletions(-)

diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index fd63071..789cf2e 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -206,7 +206,9 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig,
ecryptfs_opt_cipher, ecryptfs_opt_ecryptfs_cipher,
ecryptfs_opt_ecryptfs_key_bytes,
ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata,
- ecryptfs_opt_encrypted_view, ecryptfs_opt_err };
+ ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig,
+ ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes,
+ ecryptfs_opt_err };

static const match_table_t tokens = {
{ecryptfs_opt_sig, "sig=%s"},
@@ -217,6 +219,9 @@ static const match_table_t tokens = {
{ecryptfs_opt_passthrough, "ecryptfs_passthrough"},
{ecryptfs_opt_xattr_metadata, "ecryptfs_xattr_metadata"},
{ecryptfs_opt_encrypted_view, "ecryptfs_encrypted_view"},
+ {ecryptfs_opt_fnek_sig, "ecryptfs_fnek_sig=%s"},
+ {ecryptfs_opt_fn_cipher, "ecryptfs_fn_cipher=%s"},
+ {ecryptfs_opt_fn_cipher_key_bytes, "ecryptfs_fn_key_bytes=%u"},
{ecryptfs_opt_err, NULL}
};

@@ -281,8 +286,11 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
int rc = 0;
int sig_set = 0;
int cipher_name_set = 0;
+ int fn_cipher_name_set = 0;
int cipher_key_bytes;
int cipher_key_bytes_set = 0;
+ int fn_cipher_key_bytes;
+ int fn_cipher_key_bytes_set = 0;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(sb)->mount_crypt_stat;
substring_t args[MAX_OPT_ARGS];
@@ -290,7 +298,12 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
char *sig_src;
char *cipher_name_dst;
char *cipher_name_src;
+ char *fn_cipher_name_dst;
+ char *fn_cipher_name_src;
+ char *fnek_dst;
+ char *fnek_src;
char *cipher_key_bytes_src;
+ char *fn_cipher_key_bytes_src;

if (!options) {
rc = -EINVAL;
@@ -322,10 +335,7 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
global_default_cipher_name;
strncpy(cipher_name_dst, cipher_name_src,
ECRYPTFS_MAX_CIPHER_NAME_SIZE);
- ecryptfs_printk(KERN_DEBUG,
- "The mount_crypt_stat "
- "global_default_cipher_name set to: "
- "[%s]\n", cipher_name_dst);
+ cipher_name_dst[ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0';
cipher_name_set = 1;
break;
case ecryptfs_opt_ecryptfs_key_bytes:
@@ -335,11 +345,6 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
&cipher_key_bytes_src, 0);
mount_crypt_stat->global_default_cipher_key_size =
cipher_key_bytes;
- ecryptfs_printk(KERN_DEBUG,
- "The mount_crypt_stat "
- "global_default_cipher_key_size "
- "set to: [%d]\n", mount_crypt_stat->
- global_default_cipher_key_size);
cipher_key_bytes_set = 1;
break;
case ecryptfs_opt_passthrough:
@@ -356,11 +361,51 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
mount_crypt_stat->flags |=
ECRYPTFS_ENCRYPTED_VIEW_ENABLED;
break;
+ case ecryptfs_opt_fnek_sig:
+ fnek_src = args[0].from;
+ fnek_dst =
+ mount_crypt_stat->global_default_fnek_sig;
+ strncpy(fnek_dst, fnek_src, ECRYPTFS_SIG_SIZE_HEX);
+ mount_crypt_stat->global_default_fnek_sig[
+ ECRYPTFS_SIG_SIZE_HEX] = '\0';
+ rc = ecryptfs_add_global_auth_tok(
+ mount_crypt_stat,
+ mount_crypt_stat->global_default_fnek_sig);
+ if (rc) {
+ printk(KERN_ERR "Error attempting to register "
+ "global fnek sig [%s]; rc = [%d]\n",
+ mount_crypt_stat->global_default_fnek_sig,
+ rc);
+ goto out;
+ }
+ mount_crypt_stat->flags |=
+ (ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES
+ | ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK);
+ break;
+ case ecryptfs_opt_fn_cipher:
+ fn_cipher_name_src = args[0].from;
+ fn_cipher_name_dst =
+ mount_crypt_stat->global_default_fn_cipher_name;
+ strncpy(fn_cipher_name_dst, fn_cipher_name_src,
+ ECRYPTFS_MAX_CIPHER_NAME_SIZE);
+ mount_crypt_stat->global_default_fn_cipher_name[
+ ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0';
+ fn_cipher_name_set = 1;
+ break;
+ case ecryptfs_opt_fn_cipher_key_bytes:
+ fn_cipher_key_bytes_src = args[0].from;
+ fn_cipher_key_bytes =
+ (int)simple_strtol(fn_cipher_key_bytes_src,
+ &fn_cipher_key_bytes_src, 0);
+ mount_crypt_stat->global_default_fn_cipher_key_bytes =
+ fn_cipher_key_bytes;
+ fn_cipher_key_bytes_set = 1;
+ break;
case ecryptfs_opt_err:
default:
- ecryptfs_printk(KERN_WARNING,
- "eCryptfs: unrecognized option '%s'\n",
- p);
+ printk(KERN_WARNING
+ "%s: eCryptfs: unrecognized option [%s]\n",
+ __func__, p);
}
}
if (!sig_set) {
@@ -374,33 +419,60 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options)
int cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER);

BUG_ON(cipher_name_len >= ECRYPTFS_MAX_CIPHER_NAME_SIZE);
-
strcpy(mount_crypt_stat->global_default_cipher_name,
ECRYPTFS_DEFAULT_CIPHER);
}
- if (!cipher_key_bytes_set) {
+ if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)
+ && !fn_cipher_name_set)
+ strcpy(mount_crypt_stat->global_default_fn_cipher_name,
+ mount_crypt_stat->global_default_cipher_name);
+ if (!cipher_key_bytes_set)
mount_crypt_stat->global_default_cipher_key_size = 0;
- }
+ if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)
+ && !fn_cipher_key_bytes_set)
+ mount_crypt_stat->global_default_fn_cipher_key_bytes =
+ mount_crypt_stat->global_default_cipher_key_size;
mutex_lock(&key_tfm_list_mutex);
if (!ecryptfs_tfm_exists(mount_crypt_stat->global_default_cipher_name,
- NULL))
+ NULL)) {
rc = ecryptfs_add_new_key_tfm(
NULL, mount_crypt_stat->global_default_cipher_name,
mount_crypt_stat->global_default_cipher_key_size);
- mutex_unlock(&key_tfm_list_mutex);
- if (rc) {
- printk(KERN_ERR "Error attempting to initialize cipher with "
- "name = [%s] and key size = [%td]; rc = [%d]\n",
- mount_crypt_stat->global_default_cipher_name,
- mount_crypt_stat->global_default_cipher_key_size, rc);
- rc = -EINVAL;
- goto out;
+ if (rc) {
+ printk(KERN_ERR "Error attempting to initialize "
+ "cipher with name = [%s] and key size = [%td]; "
+ "rc = [%d]\n",
+ mount_crypt_stat->global_default_cipher_name,
+ mount_crypt_stat->global_default_cipher_key_size,
+ rc);
+ rc = -EINVAL;
+ mutex_unlock(&key_tfm_list_mutex);
+ goto out;
+ }
}
+ if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)
+ && !ecryptfs_tfm_exists(
+ mount_crypt_stat->global_default_fn_cipher_name, NULL)) {
+ rc = ecryptfs_add_new_key_tfm(
+ NULL, mount_crypt_stat->global_default_fn_cipher_name,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ if (rc) {
+ printk(KERN_ERR "Error attempting to initialize "
+ "cipher with name = [%s] and key size = [%td]; "
+ "rc = [%d]\n",
+ mount_crypt_stat->global_default_fn_cipher_name,
+ mount_crypt_stat->global_default_fn_cipher_key_bytes,
+ rc);
+ rc = -EINVAL;
+ mutex_unlock(&key_tfm_list_mutex);
+ goto out;
+ }
+ }
+ mutex_unlock(&key_tfm_list_mutex);
rc = ecryptfs_init_global_auth_toks(mount_crypt_stat);
- if (rc) {
+ if (rc)
printk(KERN_WARNING "One or more global auth toks could not "
"properly register; rc = [%d]\n", rc);
- }
out:
return rc;
}
--
1.5.3.6

2008-11-05 18:18:21

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH 3/5] eCryptfs: Filename Encryption: Encoding and encryption functions

On Tue, 2008-11-04 at 15:41 -0600, Michael Halcrow wrote:
> +static int ecryptfs_copy_filename(char **copied_name, size_t *copied_name_size,
> + const char *name, size_t name_size)
> +{
> + int rc = 0;
> +
> + (*copied_name) = kmalloc((name_size + 2), GFP_KERNEL);
> + if (!(*copied_name)) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + memcpy((void *)(*copied_name), (void *)name, name_size);
> + (*copied_name)[(name_size)] = '\0'; /* Only for convenience
> + * in printing out the
> + * string in debug
> + * messages */
> + (*copied_name_size) = (name_size + 1);
> +out:
> + return rc;
> +}

Might this be a good place to use ERR_PTR()? The pointer arithmetic
here looks a bit goofy. Is this all just to get 'copied_name_size'
returned? Why does it even matter if it is only there for printk'ing?

But, as I look at it, you do this all over the patch(es) and I think it
really reduces the readability everywhere.

If you truly need to track the actual allocated name size, I'd suggest
just having a:

struct e...c_filename {
char *name;
char *len;
};

Stack allocate that sucker just like you stack allocate
'copied_name_size', and then pass *it* around.

filename->name = foo;
filename->bar = 1234;

is way more readable than:

+ (*encoded_name) = NULL;
+ (*encoded_name_size) = 0;

and

+ (*encoded_name)[(*encoded_name_size)] = '\0';
+ (*encoded_name_size)++;

I'm certainly guilty of overly-verbose names for things, but I think:

ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE

may be a few characters too long. :)

This construct:

+ decoded_name = kmalloc(decoded_name_size, GFP_KERNEL);
+ if (!decoded_name) {
+ printk(KERN_ERR "%s: Out of memory whilst attempting "
+ "to kmalloc [%Zd] bytes\n", __func__,
+ decoded_name_size);
+ rc = -ENOMEM;
+ goto out;
+ }

also appears all over. Personally, I think this is way too verbose, but
I can also see how it would be useful. But, the crappy part is that
there is nothing unique in each of this printk()s to help the dmesg
reader to figure out what is going on.

I think you'd save yourself a lot of code if you had an
ecryptfs_kmalloc(), did the NULL check and printk() in there, and also
added a stack dump.

> rc = write_tag_66_packet(auth_tok->token.private_key.signature,
> - ecryptfs_code_for_cipher_string(crypt_stat),
> + ecryptfs_code_for_cipher_string(
> + crypt_stat->cipher,
> + crypt_stat->key_size),
> crypt_stat, &payload, &payload_len);
> if (rc) {

Why not just have ecryptfs_code_for_cipher_string() do the ->cipher and
->key_size lookup?

-- Dave

2008-11-06 07:20:48

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5] eCryptfs: Filename Encryption

On Tue 2008-11-04 15:37:54, Michael Halcrow wrote:
> This patchset implements filename encryption via a passphrase-derived
> mount-wide Filename Encryption Key (FNEK) specified as a mount
> parameter. Each encrypted filename has a fixed prefix indicating that
> eCryptfs should try to decrypt the filename. When eCryptfs
> encounters

That is 'interesting'. What happens if normal filename has that
prefix? Should some out-of-band method be used to tell normal and
encrypted filenames apart?

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2008-11-06 20:27:49

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/5] eCryptfs: Filename Encryption

On Wed, Nov 05, 2008 at 04:57:54PM +0100, Pavel Machek wrote:
> On Tue 2008-11-04 15:37:54, Michael Halcrow wrote:
> > This patchset implements filename encryption via a
> > passphrase-derived mount-wide Filename Encryption Key (FNEK)
> > specified as a mount parameter. Each encrypted filename has a
> > fixed prefix indicating that eCryptfs should try to decrypt the
> > filename. When eCryptfs encounters
>
> That is 'interesting'. What happens if normal filename has that
> prefix?

If the lower filename has the prefix but does not have a valid tag 70
packet following the prefix, then eCryptfs will complain in the syslog
and then pass through the lower filename as-is.


Attachments:
(No filename) (677.00 B)
(No filename) (489.00 B)
Download all attachments

2008-11-06 20:53:22

by Dave Kleikamp

[permalink] [raw]
Subject: Re: [PATCH 0/5] eCryptfs: Filename Encryption

On Thu, 2008-11-06 at 14:27 -0600, [email protected] wrote:
> On Wed, Nov 05, 2008 at 04:57:54PM +0100, Pavel Machek wrote:
> > On Tue 2008-11-04 15:37:54, Michael Halcrow wrote:
> > > This patchset implements filename encryption via a
> > > passphrase-derived mount-wide Filename Encryption Key (FNEK)
> > > specified as a mount parameter. Each encrypted filename has a
> > > fixed prefix indicating that eCryptfs should try to decrypt the
> > > filename. When eCryptfs encounters
> >
> > That is 'interesting'. What happens if normal filename has that
> > prefix?
>
> If the lower filename has the prefix but does not have a valid tag 70
> packet following the prefix, then eCryptfs will complain in the syslog
> and then pass through the lower filename as-is.

I'd recommend hiding this kind of syslog verbosity behind a debug config
option. I think it would be very easy to create a DOS attack against
ecryptfs by putting all sorts of clever things in the lower file system.
--
David Kleikamp
IBM Linux Technology Center

2008-11-06 21:01:47

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 3/5] eCryptfs: Filename Encryption: Encoding and encryption functions

On Wed, Nov 05, 2008 at 10:17:36AM -0800, Dave Hansen wrote:
> If you truly need to track the actual allocated name size, I'd
> suggest just having a:
>
> struct e...c_filename {
> char *name;
> char *len;
> };
>
> Stack allocate that sucker just like you stack allocate
> 'copied_name_size', and then pass *it* around.
>
> filename->name = foo;
> filename->bar = 1234;
>
> is way more readable than:
>
> + (*encoded_name) = NULL;
> + (*encoded_name_size) = 0;
>
> and
>
> + (*encoded_name)[(*encoded_name_size)] = '\0';
> + (*encoded_name_size)++;

Agreed; something akin to qstr would do well for this sort of thing
throughout eCryptfs. In addition to making it more readable, it would
also help a bit with stack usage in some places.

> I'm certainly guilty of overly-verbose names for things, but I
> think:
>
> ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE
>
> may be a few characters too long. :)

I generally try to strike a balance between verbosity and readability
with my symbol names. I frequently find myself torn between something
like:

ECRYPTFS_FILENAME_ENCRYPTION_KEY_ENCRYPTED_FILENAME_PREFIX_SIZE

And something like:

FNEKEFNPFXSZ

> This construct:
>
> + decoded_name = kmalloc(decoded_name_size, GFP_KERNEL);
> + if (!decoded_name) {
> + printk(KERN_ERR "%s: Out of memory whilst attempting "
> + "to kmalloc [%Zd] bytes\n", __func__,
> + decoded_name_size);
> + rc = -ENOMEM;
> + goto out;
> + }
>
> also appears all over. Personally, I think this is way too verbose,
> but I can also see how it would be useful. But, the crappy part is
> that there is nothing unique in each of this printk()s to help the
> dmesg reader to figure out what is going on.

There are so many colorful ways for eCryptfs to blow up that I have
found it very useful to have printk's all over the place along error
paths. On the other hand, I have yet to ever receive a bug report
indicating that a kmalloc failed, so all of these particular warnings
may be more trouble than they are worth.

> I think you'd save yourself a lot of code if you had an
> ecryptfs_kmalloc(), did the NULL check and printk() in there, and
> also added a stack dump.

I still need to set the rc value appropriate for the context and then
jump to the appropriate exit label for the location in the function at
which the kmalloc occurs. Wrapping kmalloc() can only really serve, in
general, to provide the printk and stack dump. In that case, why not
just implement a kernel-wide symbol for this (e.g.,
kmalloc_verbose())?

> > rc = write_tag_66_packet(auth_tok->token.private_key.signature,
> > - ecryptfs_code_for_cipher_string(crypt_stat),
> > + ecryptfs_code_for_cipher_string(
> > + crypt_stat->cipher,
> > + crypt_stat->key_size),
> > crypt_stat, &payload, &payload_len);
> > if (rc) {
>
> Why not just have ecryptfs_code_for_cipher_string() do the ->cipher
> and ->key_size lookup?

I changed this function in this patchset specifically to support
cipher strings and key sizes from either ecryptfs_crypt_stat or
ecryptfs_mount_crypt_stat structs.

2008-11-06 22:11:55

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/5] eCryptfs: Filename Encryption

On Thu, Nov 06, 2008 at 02:52:26PM -0600, Dave Kleikamp wrote:
> On Thu, 2008-11-06 at 14:27 -0600, [email protected] wrote:
> > On Wed, Nov 05, 2008 at 04:57:54PM +0100, Pavel Machek wrote:
> > > On Tue 2008-11-04 15:37:54, Michael Halcrow wrote:
> > > > This patchset implements filename encryption via a
> > > > passphrase-derived mount-wide Filename Encryption Key (FNEK)
> > > > specified as a mount parameter. Each encrypted filename has a
> > > > fixed prefix indicating that eCryptfs should try to decrypt the
> > > > filename. When eCryptfs encounters
> > >
> > > That is 'interesting'. What happens if normal filename has that
> > > prefix?
> >
> > If the lower filename has the prefix but does not have a valid tag
> > 70 packet following the prefix, then eCryptfs will complain in the
> > syslog and then pass through the lower filename as-is.
>
> I'd recommend hiding this kind of syslog verbosity behind a debug
> config option. I think it would be very easy to create a DOS attack
> against ecryptfs by putting all sorts of clever things in the lower
> file system.

I instead prefer leaving the default behavior as-is, while perhaps
introducing a module option to suppress such warning messages at the
user's explicit request. In general, I would imagine that the majority
of people would really like to know when a malicious attacker is
managing to write bogus mangled eCryptfs-ish files to their lower
filesystem, even if the means of transmitting the information about
the attack includes the possibility of filling up the syslogs. In
other words, if a malicious party is able to write to your filesystem
under eCryptfs, you probably have much bigger problems to worry about
than just your syslog filling up.

I can imagine some circumstances where filling up the syslog really is
worse than ignoring corrupted eCryptfs files in the lower filesystem,
but I submit that such circumstances are in the minority. I can also
contrive some circumstances where the lower files get mangled through
other non-malicious means, but I do not think such circumstances will
occur frequently enough to justify the risks from staying quiet about
file corruption (malicious or otherwise) in your lower filesystem. So,
if I had to pick a default behavior to do the greatest good for the
greatest number, I would leave the default verbosity where it is,
while providing the admin the option of disabling the verbosity when
encountering mangled lower files.

2008-11-06 22:13:31

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/5] eCryptfs: Filename Encryption: Tag 70 packets

On Tue, 4 Nov 2008 15:39:08 -0600
Michael Halcrow <[email protected]> wrote:

> A tag 70 packet contains a filename encrypted with a Filename Encryption
> Key (FNEK). This patch implements functions for writing and parsing tag
> 70 packets. This patch also adds definitions and extends structures to
> support filename encryption.
>
>
> ...
>
> +/**
> + * write_tag_70_packet - Write encrypted filename (EFN) packet against FNEK
> + * @filename: NULL-terminated filename string
> + *
> + * This is the simplest mechanism for achieving filename encryption in
> + * eCryptfs. It encrypts the given filename with the mount-wide
> + * filename encryption key (FNEK) and stores it in a packet to @dest,
> + * which the callee will encode and write directly into the dentry
> + * name.
> + */
> +int
> +ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
> + size_t *packet_size,
> + struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
> + char *filename, size_t filename_size)
> +{
> + struct ecryptfs_write_tag_70_packet_silly_stack *s;
> + int rc = 0;
> +
> + s = kmalloc(sizeof(*s), GFP_KERNEL);
> + if (!s) {
> + printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
> + "[%d] bytes of kernel memory\n", __func__, sizeof(*s));

size_t's must be printed with %zd.

> + goto out;
> + }
> + s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> + (*packet_size) = 0;
> + rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(
> + &s->desc.tfm,
> + &s->tfm_mutex, mount_crypt_stat->global_default_fn_cipher_name);
> + if (unlikely(rc)) {
> + printk(KERN_ERR "Internal error whilst attempting to get "
> + "tfm and mutex for cipher name [%s]; rc = [%d]\n",
> + mount_crypt_stat->global_default_fn_cipher_name, rc);
> + goto out;
> + }
> + mutex_lock(s->tfm_mutex);
> + s->block_size = crypto_blkcipher_blocksize(s->desc.tfm);
> + /* Plus one for the \0 separator between the random prefix
> + * and the plaintext filename */
> + s->num_rand_bytes = (ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES + 1);
> + s->block_aligned_filename_size = (s->num_rand_bytes + filename_size);
> + if ((s->block_aligned_filename_size % s->block_size) != 0) {
> + s->num_rand_bytes += (s->block_size
> + - (s->block_aligned_filename_size
> + % s->block_size));
> + s->block_aligned_filename_size = (s->num_rand_bytes
> + + filename_size);
> + }
> + /* Octet 0: Tag 70 identifier
> + * Octets 1-N1: Tag 70 packet size (includes cipher identifier
> + * and block-aligned encrypted filename size)
> + * Octets N1-N2: FNEK sig (ECRYPTFS_SIG_SIZE)
> + * Octet N2-N3: Cipher identifier (1 octet)
> + * Octets N3-N4: Block-aligned encrypted filename
> + * - Consists of a minimum number of random characters, a \0
> + * separator, and then the filename */
> + s->max_packet_size = (1 /* Tag 70 identifier */
> + + 3 /* Max Tag 70 packet size */
> + + ECRYPTFS_SIG_SIZE /* FNEK sig */
> + + 1 /* Cipher identifier */
> + + s->block_aligned_filename_size);
> + if (dest == NULL) {
> + (*packet_size) = s->max_packet_size;
> + goto out_unlock;
> + }
> + if (s->max_packet_size > (*remaining_bytes)) {
> + printk(KERN_WARNING "%s: Require [%d] bytes to write; only "
> + "[%d] available\n", __func__, s->max_packet_size,
> + (*remaining_bytes));
> + rc = -EINVAL;
> + goto out_unlock;
> + }
> + s->block_aligned_filename = kzalloc(s->block_aligned_filename_size,
> + GFP_KERNEL);
> + if (!s->block_aligned_filename) {
> + printk(KERN_ERR "%s: Out of kernel memory whilst attempting to "
> + "kzalloc [%Zd] bytes\n", __func__,

like that ;)

(I think %Z is a legacy gcc-ism, and that %z is the standard token)

In fact this patch alone adds all these warnings to the powerpc build:

fs/ecryptfs/keystore.c: In function 'ecryptfs_write_tag_70_packet':
fs/ecryptfs/keystore.c:514: warning: format '%d' expects type 'int', but argument 3 has type 'long unsigned int'
fs/ecryptfs/keystore.c:561: warning: format '%d' expects type 'int', but argument 3 has type 'size_t'
fs/ecryptfs/keystore.c:561: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c:599: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c:697: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c:707: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c: In function 'ecryptfs_parse_tag_70_packet':
fs/ecryptfs/keystore.c:790: warning: format '%d' expects type 'int', but argument 3 has type 'long unsigned int'
fs/ecryptfs/keystore.c:831: warning: format '%d' expects type 'int', but argument 3 has type 'size_t'
fs/ecryptfs/keystore.c:831: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c:864: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c:873: warning: format '%d' expects type 'int', but argument 3 has type 'size_t'
fs/ecryptfs/keystore.c:884: warning: format '%d' expects type 'int', but argument 4 has type 'size_t'
fs/ecryptfs/keystore.c:948: warning: format '%d' expects type 'int', but argument 3 has type 'size_t'

More care and better testing is needed here, please. These bugs can
and do cause machine crashes.

It looks like fixing all this will churn the patches rather a lot, so
I'll duck version #1.

>
> ...
>
> +/**
> + * parse_tag_70_packet - Parse and process FNEK-encrypted passphrase packet
> + * @filename: This function kmalloc's the memory for the filename
> + */

This kernedoc missed lots of the arguments.

> +int
> +ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
> + size_t *packet_size,
> + struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
> + char *data, size_t max_packet_size)
> +{
> + struct ecryptfs_parse_tag_70_packet_silly_stack *s;
> + int rc = 0;
> +
> + (*packet_size) = 0;
> + (*filename_size) = 0;
> + (*filename) = NULL;
> + s = kmalloc(sizeof(*s), GFP_KERNEL);
> + if (!s) {
> + printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
> + "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
> + goto out;
> + }
> + s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> + if (max_packet_size < (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)) {
> + printk(KERN_WARNING "%s: max_packet_size is [%Zd]; it must be "
> + "at least [%d]\n", __func__, max_packet_size,
> + (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1));
> + rc = -EINVAL;
> + goto out;
> + }
> + /* Octet 0: Tag 70 identifier
> + * Octets 1-N1: Tag 70 packet size (includes cipher identifier
> + * and block-aligned encrypted filename size)
> + * Octets N1-N2: FNEK sig (ECRYPTFS_SIG_SIZE)
> + * Octet N2-N3: Cipher identifier (1 octet)
> + * Octets N3-N4: Block-aligned encrypted filename
> + * - Consists of a minimum number of random numbers, a \0
> + * separator, and then the filename */
> + if (data[(*packet_size)++] != ECRYPTFS_TAG_70_PACKET_TYPE) {
> + printk(KERN_WARNING "%s: Invalid packet tag [0x%.2x]; must be "
> + "tag [0x%.2x]\n", __func__,
> + data[((*packet_size) - 1)], ECRYPTFS_TAG_70_PACKET_TYPE);
> + rc = -EINVAL;
> + goto out;
> + }
> + rc = ecryptfs_parse_packet_length(&data[(*packet_size)],
> + &s->parsed_tag_70_packet_size,
> + &s->packet_size_len);
> + if (rc) {
> + printk(KERN_WARNING "%s: Error parsing packet length; "
> + "rc = [%d]\n", __func__, rc);
> + goto out;
> + }
> + s->block_aligned_filename_size = (s->parsed_tag_70_packet_size
> + - ECRYPTFS_SIG_SIZE - 1);
> + if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
> + > max_packet_size) {
> + printk(KERN_WARNING "%s: max_packet_size is [%d]; real packet "
> + "size is [%d]\n", __func__, max_packet_size,
> + (1 + s->packet_size_len + 1
> + + s->block_aligned_filename_size));
> + rc = -EINVAL;
> + goto out;
> + }
> + (*packet_size) += s->packet_size_len;
> + ecryptfs_to_hex(s->fnek_sig_hex, &data[(*packet_size)],
> + ECRYPTFS_SIG_SIZE);
> + s->fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX] = '\0';
> + (*packet_size) += ECRYPTFS_SIG_SIZE;
> + s->cipher_code = data[(*packet_size)++];
> + rc = ecryptfs_cipher_code_to_string(s->cipher_string, s->cipher_code);
> + if (rc) {
> + printk(KERN_WARNING "%s: Cipher code [%d] is invalid\n",
> + __func__, s->cipher_code);
> + goto out;
> + }
> + rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(&s->desc.tfm,
> + &s->tfm_mutex,
> + s->cipher_string);
> + if (unlikely(rc)) {
> + printk(KERN_ERR "Internal error whilst attempting to get "
> + "tfm and mutex for cipher name [%s]; rc = [%d]\n",
> + s->cipher_string, rc);
> + goto out;
> + }
> + mutex_lock(s->tfm_mutex);
> + rc = virt_to_scatterlist(&data[(*packet_size)],
> + s->block_aligned_filename_size, &s->src_sg, 1);
> + if (rc != 1) {
> + printk(KERN_ERR "%s: Internal error whilst attempting to "
> + "convert encrypted filename memory to scatterlist; "
> + "expected rc = 1; got rc = [%d]. "
> + "block_aligned_filename_size = [%d]\n", __func__, rc,
> + s->block_aligned_filename_size);
> + goto out_unlock;
> + }
> + (*packet_size) += s->block_aligned_filename_size;
> + s->decrypted_filename = kmalloc(s->block_aligned_filename_size,
> + GFP_KERNEL);
> + if (!s->decrypted_filename) {
> + printk(KERN_ERR "%s: Out of memory whilst attempting to "
> + "kmalloc [%d] bytes\n", __func__,
> + s->block_aligned_filename_size);
> + rc = -ENOMEM;
> + goto out_unlock;
> + }
> + rc = virt_to_scatterlist(s->decrypted_filename,
> + s->block_aligned_filename_size, &s->dst_sg, 1);
> + if (rc != 1) {
> + printk(KERN_ERR "%s: Internal error whilst attempting to "
> + "convert decrypted filename memory to scatterlist; "
> + "expected rc = 1; got rc = [%d]. "
> + "block_aligned_filename_size = [%d]\n", __func__, rc,
> + s->block_aligned_filename_size);
> + goto out_free_unlock;
> + }
> + /* The characters in the first block effectively do the job of
> + * the IV here, so we just use 0's for the IV. Note the
> + * constraint that ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES
> + * >= ECRYPTFS_MAX_IV_BYTES. */
> + memset(s->iv, 0, ECRYPTFS_MAX_IV_BYTES);
> + s->desc.info = s->iv;
> + rc = ecryptfs_find_auth_tok_for_sig(&s->auth_tok, mount_crypt_stat,
> + s->fnek_sig_hex);
> + if (rc) {
> + printk(KERN_ERR "%s: Error attempting to find auth tok for "
> + "fnek sig [%s]; rc = [%d]\n", __func__, s->fnek_sig_hex,
> + rc);
> + goto out_free_unlock;
> + }
> + /* TODO: Support other key modules than passphrase for
> + * filename encryption */
> + BUG_ON(s->auth_tok->token_type != ECRYPTFS_PASSWORD);
> + rc = crypto_blkcipher_setkey(
> + s->desc.tfm,
> + s->auth_tok->token.password.session_key_encryption_key,
> + mount_crypt_stat->global_default_fn_cipher_key_bytes);
> + if (rc < 0) {
> + printk(KERN_ERR "%s: Error setting key for crypto context; "
> + "rc = [%d]. s->auth_tok->token.password.session_key_"
> + "encryption_key = [0x%p]; mount_crypt_stat->"
> + "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
> + rc,
> + s->auth_tok->token.password.session_key_encryption_key,
> + mount_crypt_stat->global_default_fn_cipher_key_bytes);
> + goto out_free_unlock;
> + }
> + rc = crypto_blkcipher_decrypt_iv(&s->desc, &s->dst_sg, &s->src_sg,
> + s->block_aligned_filename_size);
> + if (rc) {
> + printk(KERN_ERR "%s: Error attempting to decrypt filename; "
> + "rc = [%d]\n", __func__, rc);
> + goto out_free_unlock;
> + }
> + s->i = 0;
> + while (s->decrypted_filename[s->i] != '\0'
> + && s->i < s->block_aligned_filename_size)
> + s->i++;
> + if (s->i == s->block_aligned_filename_size) {
> + printk(KERN_WARNING "%s: Invalid tag 70 packet; could not "
> + "find valid separator between random characters and "
> + "the filename\n", __func__);
> + rc = -EINVAL;
> + goto out_free_unlock;
> + }
> + s->i++;
> + (*filename_size) = (s->block_aligned_filename_size - s->i);
> + if (!((*filename_size) > 0 && (*filename_size < PATH_MAX))) {
> + printk(KERN_WARNING "%s: Filename size is [%Zd], which is "
> + "invalid\n", __func__, (*filename_size));
> + rc = -EINVAL;
> + goto out_free_unlock;
> + }
> + (*filename) = kmalloc(((*filename_size) + 1), GFP_KERNEL);
> + if (!(*filename)) {
> + printk(KERN_ERR "%s: Out of memory whilst attempting to "
> + "kmalloc [%d] bytes\n", __func__,
> + ((*filename_size) + 1));
> + rc = -ENOMEM;
> + goto out_free_unlock;
> + }
> + memcpy((*filename), &s->decrypted_filename[s->i], (*filename_size));
> + (*filename)[(*filename_size)] = '\0';
> +out_free_unlock:
> + kfree(s->decrypted_filename);
> +out_unlock:
> + mutex_unlock(s->tfm_mutex);
> +out:
> + if (rc) {
> + (*packet_size) = 0;
> + (*filename_size) = 0;
> + (*filename) = NULL;
> + }
> + kfree(s);
> + return rc;
> +}

Boy, that's a tricky function.

>
> ...
>

2008-11-06 22:13:49

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 3/5] eCryptfs: Filename Encryption: Encoding and encryption functions

On Tue, 4 Nov 2008 15:41:20 -0600
Michael Halcrow <[email protected]> wrote:

> These functions support encrypting and encoding the filename
> contents. The encrypted filename contents may consist of any ASCII
> characters. This patch includes a custom encoding mechanism to map the
> ASCII characters to a reduced character set that is appropriate for
> filenames.
>
>
> ...
>
> +/* We could either offset on every reverse map or just pad some 0x00's
> + * at the front here */
> +static unsigned char filename_rev_map[] = {
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 7 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 15 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 23 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 31 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 39 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* 47 */
> + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, /* 55 */
> + 0x0A, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 63 */
> + 0x00, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, /* 71 */
> + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, /* 79 */
> + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, /* 87 */
> + 0x23, 0x24, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, /* 95 */
> + 0x00, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, /* 103 */
> + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, /* 111 */
> + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, /* 119 */
> + 0x3D, 0x3E, 0x3F
> +};

You might as well make this const also if poss. It doesn't make much
difference, apart from putting the table into write-protected memory.

>
> ...
>
> +int ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
> + const unsigned char *src, size_t src_size)
> +{
> + u8 current_bit_offset = 0;
> + size_t src_byte_offset = 0;
> + size_t dst_byte_offset = 0;
> + int rc = 0;
> +
> + if (dst == NULL) {
> + /* Not exact; conservatively long */
> + (*dst_size) = (((src_size + 1) * 3) / 4);

Are you sure about that? The destination will be 0.75 times as long as
the source? Seems risky.

What's this code doing anyway? At a guess, I'd say that it's a special
mode to this function which provides the caller with an estimate of how
much storage needs to be allocated to decode *src. Or maybe that guess
was completely wrong. A comment is needed, methinks.

> + goto out;
> + }
> + while (src_byte_offset < src_size) {
> + unsigned char src_byte =
> + filename_rev_map[(int)src[src_byte_offset]];
> +
> + switch (current_bit_offset) {
> + case 0:
> + dst[dst_byte_offset] = (src_byte << 2);
> + current_bit_offset = 6;
> + break;
> + case 6:
> + dst[dst_byte_offset++] |= (src_byte >> 4);
> + dst[dst_byte_offset] = ((src_byte & 0xF)
> + << 4);
> + current_bit_offset = 4;
> + break;
> + case 4:
> + dst[dst_byte_offset++] |= (src_byte >> 2);
> + dst[dst_byte_offset] = (src_byte << 6);
> + current_bit_offset = 2;
> + break;
> + case 2:
> + dst[dst_byte_offset++] |= (src_byte);
> + dst[dst_byte_offset] = 0;
> + current_bit_offset = 0;
> + break;
> + }
> + src_byte_offset++;
> + }
> + (*dst_size) = dst_byte_offset;
> +out:
> + return rc;
> +}
> +
>
> ...
>
> +int ecryptfs_encrypt_and_encode_filename(
> + char **encoded_name,
> + size_t *encoded_name_size,
> + struct ecryptfs_crypt_stat *crypt_stat,
> + struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
> + const char *name, size_t name_size)
> +{
> + size_t encoded_name_no_prefix_size;
> + int rc = 0;
> +
> + (*encoded_name) = NULL;
> + (*encoded_name_size) = 0;
> + if ((crypt_stat && (crypt_stat->flags & ECRYPTFS_ENCRYPT_FILENAMES))
> + || (mount_crypt_stat && (mount_crypt_stat->flags
> + & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES))) {
> + struct ecryptfs_filename *filename;
> +
> + filename = kzalloc(sizeof(*filename), GFP_KERNEL);
> + if (!filename) {
> + printk(KERN_ERR "%s: Out of memory whilst attempting "
> + "to kzalloc [%d] bytes\n", __func__,

More printk warnings :(

> + sizeof(*filename));
> + rc = -ENOMEM;
> + goto out;
> + }
> + filename->filename = (char *)name;
> + filename->filename_size = name_size;
> + rc = ecryptfs_encrypt_filename(filename, crypt_stat,
> + mount_crypt_stat);

2008-11-06 22:14:10

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 5/5] eCryptfs: Filename Encryption: mount option

On Tue, 4 Nov 2008 15:43:13 -0600
Michael Halcrow <[email protected]> wrote:

> Enable mount-wide filename encryption by providing the Filename
> Encryption Key (FNEK) signature as a mount option. Note that the
> ecryptfs-utils userspace package versions 61 or later support
> this option.

A usage example would, as always, be useful here.

Do we actually provide a secure key on the command line? If so, do any
special steps need to be taken to prevent unauthorised viewing of it?
Fancy things like `cat /proc/mounts' :)

2008-11-12 17:01:34

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH] eCryptfs: Replace %Z with %z

On Thu, Nov 06, 2008 at 02:12:34PM -0800, Andrew Morton wrote:
> (I think %Z is a legacy gcc-ism, and that %z is the standard token)

%Z is a gcc-ism. Using %z instead.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 8 ++++----
fs/ecryptfs/keystore.c | 18 +++++++++---------
fs/ecryptfs/messaging.c | 4 ++--
fs/ecryptfs/miscdev.c | 18 +++++++++---------
4 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index ea2afd2..490b129 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1679,7 +1679,7 @@ ecryptfs_encrypt_filename(struct ecryptfs_filename *filename,
kmalloc(filename->encrypted_filename_size, GFP_KERNEL);
if (!filename->encrypted_filename) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
- "to kmalloc [%Zd] bytes\n", __func__,
+ "to kmalloc [%zd] bytes\n", __func__,
filename->encrypted_filename_size);
rc = -ENOMEM;
goto out;
@@ -1752,7 +1752,7 @@ ecryptfs_process_key_cipher(struct crypto_blkcipher **key_tfm,
*key_tfm = NULL;
if (*key_size > ECRYPTFS_MAX_KEY_BYTES) {
rc = -EINVAL;
- printk(KERN_ERR "Requested key size is [%Zd] bytes; maximum "
+ printk(KERN_ERR "Requested key size is [%zd] bytes; maximum "
"allowable is [%d]\n", *key_size, ECRYPTFS_MAX_KEY_BYTES);
goto out;
}
@@ -1777,7 +1777,7 @@ ecryptfs_process_key_cipher(struct crypto_blkcipher **key_tfm,
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 "
+ 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;
@@ -2221,7 +2221,7 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
decoded_name = kmalloc(decoded_name_size, GFP_KERNEL);
if (!decoded_name) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
- "to kmalloc [%Zd] bytes\n", __func__,
+ "to kmalloc [%zd] bytes\n", __func__,
decoded_name_size);
rc = -ENOMEM;
goto out;
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 1e16893..5cfd830 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -358,7 +358,7 @@ parse_tag_67_packet(struct ecryptfs_key_record *key_rec,
/* verify that everything through the encrypted FEK size is present */
if (message_len < 4) {
rc = -EIO;
- printk(KERN_ERR "%s: message_len is [%Zd]; minimum acceptable "
+ printk(KERN_ERR "%s: message_len is [%zd]; minimum acceptable "
"message length is [%d]\n", __func__, message_len, 4);
goto out;
}
@@ -385,13 +385,13 @@ parse_tag_67_packet(struct ecryptfs_key_record *key_rec,
i += data_len;
if (message_len < (i + key_rec->enc_key_size)) {
rc = -EIO;
- printk(KERN_ERR "%s: message_len [%Zd]; max len is [%Zd]\n",
+ printk(KERN_ERR "%s: message_len [%zd]; max len is [%zd]\n",
__func__, message_len, (i + key_rec->enc_key_size));
goto out;
}
if (key_rec->enc_key_size > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
rc = -EIO;
- printk(KERN_ERR "%s: Encrypted key_size [%Zd] larger than "
+ printk(KERN_ERR "%s: Encrypted key_size [%zd] larger than "
"the maximum key size [%d]\n", __func__,
key_rec->enc_key_size,
ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
@@ -511,7 +511,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
- "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
+ "[%zd] bytes of kernel memory\n", __func__, sizeof(*s));
goto out;
}
s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
@@ -566,7 +566,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
GFP_KERNEL);
if (!s->block_aligned_filename) {
printk(KERN_ERR "%s: Out of kernel memory whilst attempting to "
- "kzalloc [%Zd] bytes\n", __func__,
+ "kzalloc [%zd] bytes\n", __func__,
s->block_aligned_filename_size);
rc = -ENOMEM;
goto out_unlock;
@@ -721,7 +721,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
printk(KERN_ERR "%s: Error setting key for crypto context; "
"rc = [%d]. s->auth_tok->token.password.session_key_"
"encryption_key = [0x%p]; mount_crypt_stat->"
- "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
+ "global_default_fn_cipher_key_bytes = [%zd]\n", __func__,
rc,
s->auth_tok->token.password.session_key_encryption_key,
mount_crypt_stat->global_default_fn_cipher_key_bytes);
@@ -792,7 +792,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
}
s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
if (max_packet_size < (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)) {
- printk(KERN_WARNING "%s: max_packet_size is [%Zd]; it must be "
+ printk(KERN_WARNING "%s: max_packet_size is [%zd]; it must be "
"at least [%d]\n", __func__, max_packet_size,
(1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1));
rc = -EINVAL;
@@ -909,7 +909,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
printk(KERN_ERR "%s: Error setting key for crypto context; "
"rc = [%d]. s->auth_tok->token.password.session_key_"
"encryption_key = [0x%p]; mount_crypt_stat->"
- "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
+ "global_default_fn_cipher_key_bytes = [%zd]\n", __func__,
rc,
s->auth_tok->token.password.session_key_encryption_key,
mount_crypt_stat->global_default_fn_cipher_key_bytes);
@@ -936,7 +936,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
s->i++;
(*filename_size) = (s->block_aligned_filename_size - s->i);
if (!((*filename_size) > 0 && (*filename_size < PATH_MAX))) {
- printk(KERN_WARNING "%s: Filename size is [%Zd], which is "
+ printk(KERN_WARNING "%s: Filename size is [%zd], which is "
"invalid\n", __func__, (*filename_size));
rc = -EINVAL;
goto out_free_unlock;
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
index e0b0a4e..a8b6cfe 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -193,7 +193,7 @@ ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, uid_t euid,
(*daemon) = kzalloc(sizeof(**daemon), GFP_KERNEL);
if (!(*daemon)) {
rc = -ENOMEM;
- printk(KERN_ERR "%s: Failed to allocate [%Zd] bytes of "
+ printk(KERN_ERR "%s: Failed to allocate [%zd] bytes of "
"GFP_KERNEL memory\n", __func__, sizeof(**daemon));
goto out;
}
@@ -435,7 +435,7 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
msg_ctx->msg = kmalloc(msg_size, GFP_KERNEL);
if (!msg_ctx->msg) {
rc = -ENOMEM;
- printk(KERN_ERR "%s: Failed to allocate [%Zd] bytes of "
+ printk(KERN_ERR "%s: Failed to allocate [%zd] bytes of "
"GFP_KERNEL memory\n", __func__, msg_size);
goto unlock;
}
diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c
index 047ac60..25ca0e1 100644
--- a/fs/ecryptfs/miscdev.c
+++ b/fs/ecryptfs/miscdev.c
@@ -203,7 +203,7 @@ int ecryptfs_send_miscdev(char *data, size_t data_size,
if (!msg_ctx->msg) {
rc = -ENOMEM;
printk(KERN_ERR "%s: Out of memory whilst attempting "
- "to kmalloc(%Zd, GFP_KERNEL)\n", __func__,
+ "to kmalloc(%zd, GFP_KERNEL)\n", __func__,
(sizeof(*msg_ctx->msg) + data_size));
goto out_unlock;
}
@@ -327,7 +327,7 @@ check_list:
if (count < total_length) {
rc = 0;
printk(KERN_WARNING "%s: Only given user buffer of "
- "size [%Zd], but we need [%Zd] to read the "
+ "size [%zd], but we need [%zd] to read the "
"pending message\n", __func__, count, total_length);
goto out_unlock_msg_ctx;
}
@@ -381,7 +381,7 @@ static int ecryptfs_miscdev_response(char *data, size_t data_size,

if ((sizeof(*msg) + msg->data_len) != data_size) {
printk(KERN_WARNING "%s: (sizeof(*msg) + msg->data_len) = "
- "[%Zd]; data_size = [%Zd]. Invalid packet.\n", __func__,
+ "[%zd]; data_size = [%zd]. Invalid packet.\n", __func__,
(sizeof(*msg) + msg->data_len), data_size);
rc = -EINVAL;
goto out;
@@ -426,7 +426,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
data = kmalloc(count, GFP_KERNEL);
if (!data) {
printk(KERN_ERR "%s: Out of memory whilst attempting to "
- "kmalloc([%Zd], GFP_KERNEL)\n", __func__, count);
+ "kmalloc([%zd], GFP_KERNEL)\n", __func__, count);
goto out;
}
rc = copy_from_user(data, buf, count);
@@ -441,8 +441,8 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
case ECRYPTFS_MSG_RESPONSE:
if (count < (1 + 4 + 1 + sizeof(struct ecryptfs_message))) {
printk(KERN_WARNING "%s: Minimum acceptable packet "
- "size is [%Zd], but amount of data written is "
- "only [%Zd]. Discarding response packet.\n",
+ "size is [%zd], but amount of data written is "
+ "only [%zd]. Discarding response packet.\n",
__func__,
(1 + 4 + 1 + sizeof(struct ecryptfs_message)),
count);
@@ -460,9 +460,9 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
}
i += packet_size_length;
if ((1 + 4 + packet_size_length + packet_size) != count) {
- printk(KERN_WARNING "%s: (1 + packet_size_length([%Zd])"
- " + packet_size([%Zd]))([%Zd]) != "
- "count([%Zd]). Invalid packet format.\n",
+ printk(KERN_WARNING "%s: (1 + packet_size_length([%zd])"
+ " + packet_size([%zd]))([%zd]) != "
+ "count([%zd]). Invalid packet format.\n",
__func__, packet_size_length, packet_size,
(1 + packet_size_length + packet_size), count);
goto out_free;
--
1.5.3.7

2008-11-12 17:04:52

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH] eCryptfs: Fix data types (int/size_t)

On Thu, Nov 06, 2008 at 02:12:34PM -0800, Andrew Morton wrote:
> On Tue, 4 Nov 2008 15:39:08 -0600
> Michael Halcrow <[email protected]> wrote:
> > + printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
> > + "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
>
> size_t's must be printed with %zd.

Correct several format string data type specifiers. Correct filename
size data types; they should be size_t rather than int when passed as
parameters to some other functions (although note that the filenames
will never be larger than int).

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 4 ++--
fs/ecryptfs/file.c | 2 +-
fs/ecryptfs/inode.c | 2 +-
fs/ecryptfs/keystore.c | 24 ++++++++++++------------
4 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 490b129..e935a22 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -2093,7 +2093,7 @@ int ecryptfs_encrypt_and_encode_filename(
filename = kzalloc(sizeof(*filename), GFP_KERNEL);
if (!filename) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
- "to kzalloc [%d] bytes\n", __func__,
+ "to kzalloc [%zd] bytes\n", __func__,
sizeof(*filename));
rc = -ENOMEM;
goto out;
@@ -2127,7 +2127,7 @@ int ecryptfs_encrypt_and_encode_filename(
(*encoded_name) = kmalloc((*encoded_name_size) + 1, GFP_KERNEL);
if (!(*encoded_name)) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
- "to kzalloc [%d] bytes\n", __func__,
+ "to kzalloc [%zd] bytes\n", __func__,
(*encoded_name_size));
rc = -ENOMEM;
kfree(filename->encrypted_filename);
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 6b0a99d..284ccb6 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -82,7 +82,7 @@ ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen,
{
struct ecryptfs_getdents_callback *buf =
(struct ecryptfs_getdents_callback *)dirent;
- int name_size;
+ size_t name_size;
char *name;
int rc;

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index cf50729..d7f449e 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -362,7 +362,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
struct nameidata *ecryptfs_nd)
{
char *encrypted_and_encoded_name = NULL;
- int encrypted_and_encoded_name_size;
+ size_t encrypted_and_encoded_name_size;
struct ecryptfs_crypt_stat *crypt_stat = NULL;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
struct ecryptfs_inode_info *inode_info;
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 5cfd830..71dcf0e 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -556,8 +556,8 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
goto out_unlock;
}
if (s->max_packet_size > (*remaining_bytes)) {
- printk(KERN_WARNING "%s: Require [%d] bytes to write; only "
- "[%d] available\n", __func__, s->max_packet_size,
+ printk(KERN_WARNING "%s: Require [%zd] bytes to write; only "
+ "[%zd] available\n", __func__, s->max_packet_size,
(*remaining_bytes));
rc = -EINVAL;
goto out_unlock;
@@ -594,7 +594,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
mount_crypt_stat->global_default_fn_cipher_key_bytes);
if (s->cipher_code == 0) {
printk(KERN_WARNING "%s: Unable to generate code for "
- "cipher [%s] with key bytes [%d]\n", __func__,
+ "cipher [%s] with key bytes [%zd]\n", __func__,
mount_crypt_stat->global_default_fn_cipher_name,
mount_crypt_stat->global_default_fn_cipher_key_bytes);
rc = -EINVAL;
@@ -693,7 +693,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
printk(KERN_ERR "%s: Internal error whilst attempting to "
"convert filename memory to scatterlist; "
"expected rc = 1; got rc = [%d]. "
- "block_aligned_filename_size = [%d]\n", __func__, rc,
+ "block_aligned_filename_size = [%zd]\n", __func__, rc,
s->block_aligned_filename_size);
goto out_release_free_unlock;
}
@@ -703,7 +703,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
printk(KERN_ERR "%s: Internal error whilst attempting to "
"convert encrypted filename memory to scatterlist; "
"expected rc = 1; got rc = [%d]. "
- "block_aligned_filename_size = [%d]\n", __func__, rc,
+ "block_aligned_filename_size = [%zd]\n", __func__, rc,
s->block_aligned_filename_size);
goto out_release_free_unlock;
}
@@ -787,7 +787,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
- "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
+ "[%zd] bytes of kernel memory\n", __func__, sizeof(*s));
goto out;
}
s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
@@ -825,8 +825,8 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
- ECRYPTFS_SIG_SIZE - 1);
if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
> max_packet_size) {
- printk(KERN_WARNING "%s: max_packet_size is [%d]; real packet "
- "size is [%d]\n", __func__, max_packet_size,
+ printk(KERN_WARNING "%s: max_packet_size is [%zd]; real packet "
+ "size is [%zd]\n", __func__, max_packet_size,
(1 + s->packet_size_len + 1
+ s->block_aligned_filename_size));
rc = -EINVAL;
@@ -860,7 +860,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
printk(KERN_ERR "%s: Internal error whilst attempting to "
"convert encrypted filename memory to scatterlist; "
"expected rc = 1; got rc = [%d]. "
- "block_aligned_filename_size = [%d]\n", __func__, rc,
+ "block_aligned_filename_size = [%zd]\n", __func__, rc,
s->block_aligned_filename_size);
goto out_unlock;
}
@@ -869,7 +869,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
GFP_KERNEL);
if (!s->decrypted_filename) {
printk(KERN_ERR "%s: Out of memory whilst attempting to "
- "kmalloc [%d] bytes\n", __func__,
+ "kmalloc [%zd] bytes\n", __func__,
s->block_aligned_filename_size);
rc = -ENOMEM;
goto out_unlock;
@@ -880,7 +880,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
printk(KERN_ERR "%s: Internal error whilst attempting to "
"convert decrypted filename memory to scatterlist; "
"expected rc = 1; got rc = [%d]. "
- "block_aligned_filename_size = [%d]\n", __func__, rc,
+ "block_aligned_filename_size = [%zd]\n", __func__, rc,
s->block_aligned_filename_size);
goto out_free_unlock;
}
@@ -944,7 +944,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
(*filename) = kmalloc(((*filename_size) + 1), GFP_KERNEL);
if (!(*filename)) {
printk(KERN_ERR "%s: Out of memory whilst attempting to "
- "kmalloc [%d] bytes\n", __func__,
+ "kmalloc [%zd] bytes\n", __func__,
((*filename_size) + 1));
rc = -ENOMEM;
goto out_free_unlock;
--
1.5.3.7

2008-11-12 17:06:59

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH] eCryptfs: kerneldoc for ecryptfs_parse_tag_70_packet()

On Thu, Nov 06, 2008 at 02:12:34PM -0800, Andrew Morton wrote:
> On Tue, 4 Nov 2008 15:39:08 -0600
> Michael Halcrow <[email protected]> wrote:
> > +/**
> > + * parse_tag_70_packet - Parse and process FNEK-encrypted passphrase packet
> > + * @filename: This function kmalloc's the memory for the filename
> > + */
>
> This kernedoc missed lots of the arguments.

Kerneldoc updates for ecryptfs_parse_tag_70_packet().

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/keystore.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 71dcf0e..3c90250 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -771,6 +771,17 @@ struct ecryptfs_parse_tag_70_packet_silly_stack {
/**
* parse_tag_70_packet - Parse and process FNEK-encrypted passphrase packet
* @filename: This function kmalloc's the memory for the filename
+ * @filename_size: This function sets this to the amount of memory
+ * kmalloc'd for the filename
+ * @packet_size: This function sets this to the the number of octets
+ * in the packet parsed
+ * @mount_crypt_stat: The mount-wide cryptographic context
+ * @data: The memory location containing the start of the tag 70
+ * packet
+ * @max_packet_size: The maximum legal size of the packet to be parsed
+ * from @data
+ *
+ * Returns zero on success; non-zero otherwise
*/
int
ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
--
1.5.3.7

2008-11-12 17:11:38

by Michael Halcrow

[permalink] [raw]
Subject: [PATCH] eCryptfs: Clean up ecryptfs_decode_from_filename()

On Thu, Nov 06, 2008 at 02:12:54PM -0800, Andrew Morton wrote:
> On Tue, 4 Nov 2008 15:41:20 -0600
> Michael Halcrow <[email protected]> wrote:
> > +static unsigned char filename_rev_map[] = {
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 7 */
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 15 */
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 23 */
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 31 */
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 39 */
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* 47 */
> > + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, /* 55 */
> > + 0x0A, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 63 */
> > + 0x00, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, /* 71 */
> > + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, /* 79 */
> > + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, /* 87 */
> > + 0x23, 0x24, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, /* 95 */
> > + 0x00, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, /* 103 */
> > + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, /* 111 */
> > + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, /* 119 */
> > + 0x3D, 0x3E, 0x3F
> > +};
>
> You might as well make this const also if poss. It doesn't make much
> difference, apart from putting the table into write-protected
> memory.
> >
> > ...
> >
> > +int ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
> > + const unsigned char *src, size_t src_size)
> > +{
> > + u8 current_bit_offset = 0;
> > + size_t src_byte_offset = 0;
> > + size_t dst_byte_offset = 0;
> > + int rc = 0;
> > +
> > + if (dst == NULL) {
> > + /* Not exact; conservatively long */
> > + (*dst_size) = (((src_size + 1) * 3) / 4);
>
> Are you sure about that? The destination will be 0.75 times as long
> as the source? Seems risky.
>
> What's this code doing anyway? At a guess, I'd say that it's a
> special mode to this function which provides the caller with an
> estimate of how much storage needs to be allocated to decode *src.
> Or maybe that guess was completely wrong. A comment is needed,
> methinks.

Flesh out the comments for ecryptfs_decode_from_filename(). Remove the
return condition, since it is always 0.

Signed-off-by: Michael Halcrow <[email protected]>
---
fs/ecryptfs/crypto.c | 51 +++++++++++++++++++++++--------------------------
1 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index e935a22..c01e043 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1931,7 +1931,7 @@ static unsigned char *portable_filename_chars = ("-.0123456789ABCD"

/* We could either offset on every reverse map or just pad some 0x00's
* at the front here */
-static unsigned char filename_rev_map[] = {
+static const unsigned char filename_rev_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 15 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 23 */
@@ -2012,16 +2012,30 @@ out:
return;
}

-int ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
- const unsigned char *src, size_t src_size)
+/**
+ * ecryptfs_decode_from_filename
+ * @dst: If NULL, this function only sets @dst_size and returns. If
+ * non-NULL, this function decodes the encoded octets in @src
+ * into the memory that @dst points to.
+ * @dst_size: Set to the size of the decoded string.
+ * @src: The encoded set of octets to decode.
+ * @src_size: The size of the encoded set of octets to decode.
+ */
+static void
+ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
+ const unsigned char *src, size_t src_size)
{
u8 current_bit_offset = 0;
size_t src_byte_offset = 0;
size_t dst_byte_offset = 0;
- int rc = 0;

if (dst == NULL) {
- /* Not exact; conservatively long */
+ /* Not exact; conservatively long. Every block of 4
+ * encoded characters decodes into a block of 3
+ * decoded characters. This segment of code provides
+ * the caller with the maximum amount of allocated
+ * space that @dst will need to point to in a
+ * subsequent call. */
(*dst_size) = (((src_size + 1) * 3) / 4);
goto out;
}
@@ -2055,7 +2069,7 @@ int ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
}
(*dst_size) = dst_byte_offset;
out:
- return rc;
+ return;
}

/**
@@ -2208,16 +2222,8 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,

name += ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE;
name_size -= ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE;
- rc = ecryptfs_decode_from_filename(NULL, &decoded_name_size,
- name, name_size);
- if (rc) {
- printk(KERN_ERR "%s: Error attempting to decode "
- "filename; rc = [%d]\n", __func__, rc);
- rc = ecryptfs_copy_filename(plaintext_name,
- plaintext_name_size,
- orig_name, orig_name_size);
- goto out;
- }
+ ecryptfs_decode_from_filename(NULL, &decoded_name_size,
+ name, name_size);
decoded_name = kmalloc(decoded_name_size, GFP_KERNEL);
if (!decoded_name) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
@@ -2226,17 +2232,8 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
rc = -ENOMEM;
goto out;
}
- rc = ecryptfs_decode_from_filename(decoded_name,
- &decoded_name_size,
- name, name_size);
- if (rc) {
- printk(KERN_ERR "%s: Error attempting to decode "
- "filename; rc = [%d]\n", __func__, rc);
- rc = ecryptfs_copy_filename(plaintext_name,
- plaintext_name_size,
- orig_name, orig_name_size);
- goto out_free;
- }
+ ecryptfs_decode_from_filename(decoded_name, &decoded_name_size,
+ name, name_size);
rc = ecryptfs_parse_tag_70_packet(plaintext_name,
plaintext_name_size,
&packet_size,
--
1.5.3.7

2008-11-14 16:47:29

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 5/5] eCryptfs: Filename Encryption: mount option

On Thu, Nov 06, 2008 at 02:13:04PM -0800, Andrew Morton wrote:
> On Tue, 4 Nov 2008 15:43:13 -0600
> Michael Halcrow <[email protected]> wrote:
> > Enable mount-wide filename encryption by providing the Filename
> > Encryption Key (FNEK) signature as a mount option. Note that the
> > ecryptfs-utils userspace package versions 61 or later support this
> > option.
>
> A usage example would, as always, be useful here.

When mounting with ecryptfs-utils version 61 or later, the mount
helper will detect the availability of the passphrase-based filename
encryption in the kernel (via the eCryptfs sysfs handle) and query the
user interactively as to whether or not he wants to enable the feature
for the mount. If the user enables filename encryption, the mount
helper will then prompt for the FNEK signature that the user wishes to
use, suggesting by default the signature for the mount passphrase that
the user has already entered for encrypting the file contents.

When not using the mount helper, the user can specify the signature
for the passphrase key with the ecryptfs_fnek_sig= mount option. This
key must be available in the user's keyring. The mount helper usually
takes care of this step. If, however, the user is not mounting with
the mount helper, then he will need to enter the passphrase key into
his keyring with some other utility prior to mounting, such as
ecryptfs-manager.

> Do we actually provide a secure key on the command line? If so, do
> any special steps need to be taken to prevent unauthorised viewing
> of it? Fancy things like `cat /proc/mounts' :)

The mount helper does things correctly by default. Never at any time
does anything provide an actual secret value to eCryptfs as a mount
parameter.