ext4 crypto: optimize filename encryption
Encrypt the filename as soon it is passed in by the user. This avoids our needing to encrypt the filename 2 or 3 times while in the process of creating a filename. Similarly, when looking up a directory entry, encrypt the filename early, or if the encryption key is not available, base-64 decode the file syystem so that the hash value and the last 16 bytes of the encrypted filename is available in the new struct ext4_filename data structure. Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
@@ -611,109 +611,82 @@ int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the htree hash from a filename from user space
|
||||
*/
|
||||
int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct qstr *iname,
|
||||
struct dx_hash_info *hinfo)
|
||||
int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct ext4_filename *fname)
|
||||
{
|
||||
struct ext4_str tmp;
|
||||
int ret = 0;
|
||||
char buf[EXT4_FNAME_CRYPTO_DIGEST_SIZE+1];
|
||||
struct ext4_fname_crypto_ctx *ctx;
|
||||
int ret = 0, bigname = 0;
|
||||
|
||||
if (!ctx ||
|
||||
memset(fname, 0, sizeof(struct ext4_filename));
|
||||
fname->usr_fname = iname;
|
||||
|
||||
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
if ((ctx == NULL) ||
|
||||
((iname->name[0] == '.') &&
|
||||
((iname->len == 1) ||
|
||||
((iname->name[1] == '.') && (iname->len == 2))))) {
|
||||
ext4fs_dirhash(iname->name, iname->len, hinfo);
|
||||
return 0;
|
||||
fname->disk_name.name = (unsigned char *) iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ctx->has_valid_key && iname->name[0] == '_') {
|
||||
if (iname->len != 33)
|
||||
return -ENOENT;
|
||||
ret = digest_decode(iname->name+1, iname->len, buf);
|
||||
if (ret != 24)
|
||||
return -ENOENT;
|
||||
memcpy(&hinfo->hash, buf, 4);
|
||||
memcpy(&hinfo->minor_hash, buf + 4, 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx->has_valid_key && iname->name[0] != '_') {
|
||||
if (iname->len > 43)
|
||||
return -ENOENT;
|
||||
ret = digest_decode(iname->name, iname->len, buf);
|
||||
ext4fs_dirhash(buf, ret, hinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First encrypt the plaintext name */
|
||||
ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len, &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ext4_fname_encrypt(ctx, iname, &tmp);
|
||||
if (ret >= 0) {
|
||||
ext4fs_dirhash(tmp.name, tmp.len, hinfo);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
ext4_fname_crypto_free_buffer(&tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
|
||||
int len, const char * const name,
|
||||
struct ext4_dir_entry_2 *de)
|
||||
{
|
||||
int ret = -ENOENT;
|
||||
int bigname = (*name == '_');
|
||||
|
||||
if (ctx->has_valid_key) {
|
||||
if (cstr->name == NULL) {
|
||||
struct qstr istr;
|
||||
|
||||
ret = ext4_fname_crypto_alloc_buffer(ctx, len, cstr);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
istr.name = name;
|
||||
istr.len = len;
|
||||
ret = ext4_fname_encrypt(ctx, &istr, cstr);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
if (cstr->name == NULL) {
|
||||
cstr->name = kmalloc(32, GFP_KERNEL);
|
||||
if (cstr->name == NULL)
|
||||
return -ENOMEM;
|
||||
if ((bigname && (len != 33)) ||
|
||||
(!bigname && (len > 43)))
|
||||
goto errout;
|
||||
ret = digest_decode(name+bigname, len-bigname,
|
||||
cstr->name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
cstr->len = ret;
|
||||
}
|
||||
if (bigname) {
|
||||
if (de->name_len < 16)
|
||||
return 0;
|
||||
ret = memcmp(de->name + de->name_len - 16,
|
||||
cstr->name + 8, 16);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
}
|
||||
ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len,
|
||||
&fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = ext4_fname_encrypt(ctx, iname, &fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (de->name_len != cstr->len)
|
||||
return 0;
|
||||
ret = memcmp(de->name, cstr->name, cstr->len);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
errout:
|
||||
kfree(cstr->name);
|
||||
cstr->name = NULL;
|
||||
if (!lookup) {
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We don't have the key and we are doing a lookup; decode the
|
||||
* user-supplied name
|
||||
*/
|
||||
if (iname->name[0] == '_')
|
||||
bigname = 1;
|
||||
if ((bigname && (iname->len != 33)) ||
|
||||
(!bigname && (iname->len > 43))) {
|
||||
ret = -ENOENT;
|
||||
}
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (bigname) {
|
||||
memcpy(&fname->hinfo.hash, fname->crypto_buf.name, 4);
|
||||
memcpy(&fname->hinfo.minor_hash, fname->crypto_buf.name + 4, 4);
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
ext4_put_fname_crypto_ctx(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ext4_fname_free_filename(struct ext4_filename *fname)
|
||||
{
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
}
|
||||
|
Reference in New Issue
Block a user