Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2299590 authored by Theodore Ts'o's avatar Theodore Ts'o
Browse files

ext4 crypto: don't allocate a page when encrypting/decrypting file names



Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 5b643f9c
Loading
Loading
Loading
Loading
+20 −52
Original line number Diff line number Diff line
@@ -65,9 +65,9 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
	struct crypto_ablkcipher *tfm = ctx->ctfm;
	int res = 0;
	char iv[EXT4_CRYPTO_BLOCK_SIZE];
	struct scatterlist sg[1];
	struct scatterlist src_sg, dst_sg;
	int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
	char *workbuf;
	char *workbuf, buf[32], *alloc_buf = NULL;

	if (iname->len <= 0 || iname->len > ctx->lim)
		return -EIO;
@@ -78,20 +78,27 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
	ciphertext_len = (ciphertext_len > ctx->lim)
			? ctx->lim : ciphertext_len;

	if (ciphertext_len <= sizeof(buf)) {
		workbuf = buf;
	} else {
		alloc_buf = kmalloc(ciphertext_len, GFP_NOFS);
		if (!alloc_buf)
			return -ENOMEM;
		workbuf = alloc_buf;
	}

	/* Allocate request */
	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
	if (!req) {
		printk_ratelimited(
		    KERN_ERR "%s: crypto_request_alloc() failed\n", __func__);
		kfree(alloc_buf);
		return -ENOMEM;
	}
	ablkcipher_request_set_callback(req,
		CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
		ext4_dir_crypt_complete, &ecr);

	/* Map the workpage */
	workbuf = kmap(ctx->workpage);

	/* Copy the input */
	memcpy(workbuf, iname->name, iname->len);
	if (iname->len < ciphertext_len)
@@ -101,21 +108,16 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
	memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE);

	/* Create encryption request */
	sg_init_table(sg, 1);
	sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0);
	ablkcipher_request_set_crypt(req, sg, sg, ciphertext_len, iv);
	sg_init_one(&src_sg, workbuf, ciphertext_len);
	sg_init_one(&dst_sg, oname->name, ciphertext_len);
	ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
	res = crypto_ablkcipher_encrypt(req);
	if (res == -EINPROGRESS || res == -EBUSY) {
		BUG_ON(req->base.data != &ecr);
		wait_for_completion(&ecr.completion);
		res = ecr.res;
	}
	if (res >= 0) {
		/* Copy the result to output */
		memcpy(oname->name, workbuf, ciphertext_len);
		res = ciphertext_len;
	}
	kunmap(ctx->workpage);
	kfree(alloc_buf);
	ablkcipher_request_free(req);
	if (res < 0) {
		printk_ratelimited(
@@ -139,11 +141,10 @@ static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx,
	struct ext4_str tmp_in[2], tmp_out[1];
	struct ablkcipher_request *req = NULL;
	DECLARE_EXT4_COMPLETION_RESULT(ecr);
	struct scatterlist sg[1];
	struct scatterlist src_sg, dst_sg;
	struct crypto_ablkcipher *tfm = ctx->ctfm;
	int res = 0;
	char iv[EXT4_CRYPTO_BLOCK_SIZE];
	char *workbuf;

	if (iname->len <= 0 || iname->len > ctx->lim)
		return -EIO;
@@ -163,31 +164,19 @@ static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx,
		CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
		ext4_dir_crypt_complete, &ecr);

	/* Map the workpage */
	workbuf = kmap(ctx->workpage);

	/* Copy the input */
	memcpy(workbuf, iname->name, iname->len);

	/* Initialize IV */
	memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE);

	/* Create encryption request */
	sg_init_table(sg, 1);
	sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0);
	ablkcipher_request_set_crypt(req, sg, sg, iname->len, iv);
	sg_init_one(&src_sg, iname->name, iname->len);
	sg_init_one(&dst_sg, oname->name, oname->len);
	ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
	res = crypto_ablkcipher_decrypt(req);
	if (res == -EINPROGRESS || res == -EBUSY) {
		BUG_ON(req->base.data != &ecr);
		wait_for_completion(&ecr.completion);
		res = ecr.res;
	}
	if (res >= 0) {
		/* Copy the result to output */
		memcpy(oname->name, workbuf, iname->len);
		res = iname->len;
	}
	kunmap(ctx->workpage);
	ablkcipher_request_free(req);
	if (res < 0) {
		printk_ratelimited(
@@ -267,8 +256,6 @@ void ext4_free_fname_crypto_ctx(struct ext4_fname_crypto_ctx *ctx)
		crypto_free_ablkcipher(ctx->ctfm);
	if (ctx->htfm && !IS_ERR(ctx->htfm))
		crypto_free_hash(ctx->htfm);
	if (ctx->workpage && !IS_ERR(ctx->workpage))
		__free_page(ctx->workpage);
	kfree(ctx);
}

@@ -322,7 +309,6 @@ struct ext4_fname_crypto_ctx *ext4_alloc_fname_crypto_ctx(
	ctx->ctfm_key_is_ready = 0;
	ctx->ctfm = NULL;
	ctx->htfm = NULL;
	ctx->workpage = NULL;
	return ctx;
}

@@ -390,24 +376,6 @@ struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(
			ext4_put_fname_crypto_ctx(&ctx);
			return ERR_PTR(-ENOMEM);
		}
		if (ctx->workpage == NULL)
			ctx->workpage = alloc_page(GFP_NOFS);
		if (IS_ERR(ctx->workpage)) {
			res = PTR_ERR(ctx->workpage);
			printk(
			    KERN_DEBUG "%s: error (%d) allocating work page\n",
			    __func__, res);
			ctx->workpage = NULL;
			ext4_put_fname_crypto_ctx(&ctx);
			return ERR_PTR(res);
		}
		if (ctx->workpage == NULL) {
			printk(
			    KERN_DEBUG "%s: could not allocate work page\n",
			    __func__);
			ext4_put_fname_crypto_ctx(&ctx);
			return ERR_PTR(-ENOMEM);
		}
		ctx->lim = max_ciphertext_len;
		crypto_ablkcipher_clear_flags(ctx->ctfm, ~0);
		crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctx->ctfm),
+3 −0
Original line number Diff line number Diff line
@@ -247,9 +247,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
					    get_dtype(sb, de->file_type)))
						goto done;
				} else {
					int save_len = fname_crypto_str.len;

					/* Directory is encrypted */
					err = ext4_fname_disk_to_usr(enc_ctx,
						NULL, de, &fname_crypto_str);
					fname_crypto_str.len = save_len;
					if (err < 0)
						goto errout;
					if (!dir_emit(ctx,
+0 −2
Original line number Diff line number Diff line
@@ -123,10 +123,8 @@ struct ext4_str {

struct ext4_fname_crypto_ctx {
	u32 lim;
	char tmp_buf[EXT4_CRYPTO_BLOCK_SIZE];
	struct crypto_ablkcipher *ctfm;
	struct crypto_hash *htfm;
	struct page *workpage;
	struct ext4_encryption_key key;
	unsigned flags : 8;
	unsigned has_valid_key : 1;
+4 −0
Original line number Diff line number Diff line
@@ -998,6 +998,8 @@ static int htree_dirblock_to_tree(struct file *dir_file,
				   hinfo->hash, hinfo->minor_hash, de,
				   &tmp_str);
		} else {
			int save_len = fname_crypto_str.len;

			/* Directory is encrypted */
			err = ext4_fname_disk_to_usr(ctx, hinfo, de,
						     &fname_crypto_str);
@@ -1008,6 +1010,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
			err = ext4_htree_store_dirent(dir_file,
				   hinfo->hash, hinfo->minor_hash, de,
					&fname_crypto_str);
			fname_crypto_str.len = save_len;
		}
		if (err != 0) {
			count = err;
@@ -3126,6 +3129,7 @@ static int ext4_symlink(struct inode *dir,
		istr.name = (const unsigned char *) symname;
		istr.len = len;
		ostr.name = sd->encrypted_path;
		ostr.len = disk_link.len;
		err = ext4_fname_usr_to_disk(ctx, &istr, &ostr);
		ext4_put_fname_crypto_ctx(&ctx);
		if (err < 0)
+1 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
		goto errout;
	}
	pstr.name = paddr;
	pstr.len = plen;
	res = _ext4_fname_disk_to_usr(ctx, NULL, &cstr, &pstr);
	if (res < 0)
		goto errout;