Commit 4a26620d authored by Tyler Hicks's avatar Tyler Hicks
Browse files

eCryptfs: Improve statfs reporting

statfs() calls on eCryptfs files returned the wrong filesystem type and,
when using filename encryption, the wrong maximum filename length.

If mount-wide filename encryption is enabled, the cipher block size and
the lower filesystem's max filename length will determine the max
eCryptfs filename length. Pre-tested, known good lengths are used when
the lower filesystem's namelen is 255 and a cipher with 8 or 16 byte
block sizes is used. In other, less common cases, we fall back to a safe
rounded-down estimate when determining the eCryptfs namelen.

https://launchpad.net/bugs/885744

Signed-off-by: default avatarTyler Hicks <tyhicks@canonical.com>
Reported-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent c38e2345
......@@ -1990,6 +1990,17 @@ void ecryptfs_encode_for_filename(unsigned char *dst, size_t *dst_size,
return;
}
static size_t ecryptfs_max_decoded_size(size_t encoded_size)
{
/* 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. */
return ((encoded_size + 1) * 3) / 4;
}
/**
* ecryptfs_decode_from_filename
* @dst: If NULL, this function only sets @dst_size and returns. If
......@@ -2008,13 +2019,7 @@ ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size,
size_t dst_byte_offset = 0;
if (dst == NULL) {
/* 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);
(*dst_size) = ecryptfs_max_decoded_size(src_size);
goto out;
}
while (src_byte_offset < src_size) {
......@@ -2239,3 +2244,52 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
out:
return rc;
}
#define ENC_NAME_MAX_BLOCKLEN_8_OR_16 143
int ecryptfs_set_f_namelen(long *namelen, long lower_namelen,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
{
struct blkcipher_desc desc;
struct mutex *tfm_mutex;
size_t cipher_blocksize;
int rc;
if (!(mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)) {
(*namelen) = lower_namelen;
return 0;
}
rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(&desc.tfm, &tfm_mutex,
mount_crypt_stat->global_default_fn_cipher_name);
if (unlikely(rc)) {
(*namelen) = 0;
return rc;
}
mutex_lock(tfm_mutex);
cipher_blocksize = crypto_blkcipher_blocksize(desc.tfm);
mutex_unlock(tfm_mutex);
/* Return an exact amount for the common cases */
if (lower_namelen == NAME_MAX
&& (cipher_blocksize == 8 || cipher_blocksize == 16)) {
(*namelen) = ENC_NAME_MAX_BLOCKLEN_8_OR_16;
return 0;
}
/* Return a safe estimate for the uncommon cases */
(*namelen) = lower_namelen;
(*namelen) -= ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE;
/* Since this is the max decoded size, subtract 1 "decoded block" len */
(*namelen) = ecryptfs_max_decoded_size(*namelen) - 3;
(*namelen) -= ECRYPTFS_TAG_70_MAX_METADATA_SIZE;
(*namelen) -= ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES;
/* Worst case is that the filename is padded nearly a full block size */
(*namelen) -= cipher_blocksize - 1;
if ((*namelen) < 0)
(*namelen) = 0;
return 0;
}
......@@ -162,6 +162,10 @@ ecryptfs_get_key_payload_data(struct key *key)
#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_TAG_70_MIN_METADATA_SIZE (1 + ECRYPTFS_MIN_PKT_LEN_SIZE \
+ ECRYPTFS_SIG_SIZE + 1 + 1)
#define ECRYPTFS_TAG_70_MAX_METADATA_SIZE (1 + ECRYPTFS_MAX_PKT_LEN_SIZE \
+ ECRYPTFS_SIG_SIZE + 1 + 1)
#define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FEK_ENCRYPTED."
#define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX_SIZE 23
#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FNEK_ENCRYPTED."
......@@ -701,6 +705,8 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
size_t *packet_size,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
char *data, size_t max_packet_size);
int ecryptfs_set_f_namelen(long *namelen, long lower_namelen,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat);
int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
loff_t offset);
......
......@@ -679,10 +679,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
* 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->max_packet_size = (ECRYPTFS_TAG_70_MAX_METADATA_SIZE
+ s->block_aligned_filename_size);
if (dest == NULL) {
(*packet_size) = s->max_packet_size;
......@@ -934,10 +931,10 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
goto out;
}
s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
if (max_packet_size < (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)) {
if (max_packet_size < ECRYPTFS_TAG_70_MIN_METADATA_SIZE) {
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));
ECRYPTFS_TAG_70_MIN_METADATA_SIZE);
rc = -EINVAL;
goto out;
}
......
......@@ -30,6 +30,8 @@
#include <linux/seq_file.h>
#include <linux/file.h>
#include <linux/crypto.h>
#include <linux/statfs.h>
#include <linux/magic.h>
#include "ecryptfs_kernel.h"
struct kmem_cache *ecryptfs_inode_info_cache;
......@@ -102,10 +104,20 @@ static void ecryptfs_destroy_inode(struct inode *inode)
static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
int rc;
if (!lower_dentry->d_sb->s_op->statfs)
return -ENOSYS;
return lower_dentry->d_sb->s_op->statfs(lower_dentry, buf);
rc = lower_dentry->d_sb->s_op->statfs(lower_dentry, buf);
if (rc)
return rc;
buf->f_type = ECRYPTFS_SUPER_MAGIC;
rc = ecryptfs_set_f_namelen(&buf->f_namelen, buf->f_namelen,
&ecryptfs_superblock_to_private(dentry->d_sb)->mount_crypt_stat);
return rc;
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment