You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o: "The major change this cycle is deleting ext4's copy of the file system encryption code and switching things over to using the copies in fs/crypto. I've updated the MAINTAINERS file to add an entry for fs/crypto listing Jaeguk Kim and myself as the maintainers. There are also a number of bug fixes, most notably for some problems found by American Fuzzy Lop (AFL) courtesy of Vegard Nossum. Also fixed is a writeback deadlock detected by generic/130, and some potential races in the metadata checksum code" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (21 commits) ext4: verify extent header depth ext4: short-cut orphan cleanup on error ext4: fix reference counting bug on block allocation error MAINTAINRES: fs-crypto maintainers update ext4 crypto: migrate into vfs's crypto engine ext2: fix filesystem deadlock while reading corrupted xattr block ext4: fix project quota accounting without quota limits enabled ext4: validate s_reserved_gdt_blocks on mount ext4: remove unused page_idx ext4: don't call ext4_should_journal_data() on the journal inode ext4: Fix WARN_ON_ONCE in ext4_commit_super() ext4: fix deadlock during page writeback ext4: correct error value of function verifying dx checksum ext4: avoid modifying checksum fields directly during checksum verification ext4: check for extents that wrap around jbd2: make journal y2038 safe jbd2: track more dependencies on transaction commit jbd2: move lockdep tracking to journal_s jbd2: move lockdep instrumentation for jbd2 handles ext4: respect the nobarrier mount option in nojournal mode ...
This commit is contained in:
@@ -4942,6 +4942,13 @@ F: Documentation/filesystems/caching/
|
||||
F: fs/fscache/
|
||||
F: include/linux/fscache*.h
|
||||
|
||||
FS-CRYPTO: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
|
||||
M: Theodore Y. Ts'o <tytso@mit.edu>
|
||||
M: Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
S: Supported
|
||||
F: fs/crypto/
|
||||
F: include/linux/fscrypto.h
|
||||
|
||||
F2FS FILE SYSTEM
|
||||
M: Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
M: Changman Lee <cm224.lee@samsung.com>
|
||||
|
||||
@@ -1193,6 +1193,27 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if the passed-in block region is valid; 0 if some part overlaps
|
||||
* with filesystem metadata blocksi.
|
||||
*/
|
||||
int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk,
|
||||
unsigned int count)
|
||||
{
|
||||
if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
|
||||
(start_blk + count < start_blk) ||
|
||||
(start_blk > le32_to_cpu(sbi->s_es->s_blocks_count)))
|
||||
return 0;
|
||||
|
||||
/* Ensure we do not step over superblock */
|
||||
if ((start_blk <= sbi->s_sb_block) &&
|
||||
(start_blk + count >= sbi->s_sb_block))
|
||||
return 0;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ext2_new_blocks() -- core block(s) allocation function
|
||||
* @inode: file inode
|
||||
|
||||
@@ -367,6 +367,7 @@ struct ext2_inode {
|
||||
*/
|
||||
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
|
||||
#define EXT2_ERROR_FS 0x0002 /* Errors detected */
|
||||
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
|
||||
|
||||
/*
|
||||
* Mount flags
|
||||
@@ -739,6 +740,8 @@ extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
|
||||
extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *);
|
||||
extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long,
|
||||
unsigned long *, int *);
|
||||
extern int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk,
|
||||
unsigned int count);
|
||||
extern void ext2_free_blocks (struct inode *, unsigned long,
|
||||
unsigned long);
|
||||
extern unsigned long ext2_count_free_blocks (struct super_block *);
|
||||
|
||||
@@ -1389,6 +1389,16 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
|
||||
ei->i_frag_size = raw_inode->i_fsize;
|
||||
ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
|
||||
ei->i_dir_acl = 0;
|
||||
|
||||
if (ei->i_file_acl &&
|
||||
!ext2_data_block_valid(EXT2_SB(sb), ei->i_file_acl, 1)) {
|
||||
ext2_error(sb, "ext2_iget", "bad extended attribute block %u",
|
||||
ei->i_file_acl);
|
||||
brelse(bh);
|
||||
ret = -EFSCORRUPTED;
|
||||
goto bad_inode;
|
||||
}
|
||||
|
||||
if (S_ISREG(inode->i_mode))
|
||||
inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
|
||||
else
|
||||
|
||||
@@ -759,10 +759,19 @@ void
|
||||
ext2_xattr_delete_inode(struct inode *inode)
|
||||
{
|
||||
struct buffer_head *bh = NULL;
|
||||
struct ext2_sb_info *sbi = EXT2_SB(inode->i_sb);
|
||||
|
||||
down_write(&EXT2_I(inode)->xattr_sem);
|
||||
if (!EXT2_I(inode)->i_file_acl)
|
||||
goto cleanup;
|
||||
|
||||
if (!ext2_data_block_valid(sbi, EXT2_I(inode)->i_file_acl, 0)) {
|
||||
ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
|
||||
"inode %ld: xattr block %d is out of data blocks range",
|
||||
inode->i_ino, EXT2_I(inode)->i_file_acl);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl);
|
||||
if (!bh) {
|
||||
ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
|
||||
|
||||
+2
-10
@@ -99,17 +99,9 @@ config EXT4_FS_SECURITY
|
||||
extended attributes for file security labels, say N.
|
||||
|
||||
config EXT4_ENCRYPTION
|
||||
tristate "Ext4 Encryption"
|
||||
bool "Ext4 Encryption"
|
||||
depends on EXT4_FS
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
select ENCRYPTED_KEYS
|
||||
select FS_ENCRYPTION
|
||||
help
|
||||
Enable encryption of ext4 files and directories. This
|
||||
feature is similar to ecryptfs, but it is more memory
|
||||
|
||||
@@ -12,5 +12,3 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
|
||||
|
||||
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
|
||||
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
|
||||
ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \
|
||||
crypto_key.o crypto_fname.o
|
||||
|
||||
+6
-1
@@ -208,6 +208,9 @@ static int ext4_init_block_bitmap(struct super_block *sb,
|
||||
memset(bh->b_data, 0, sb->s_blocksize);
|
||||
|
||||
bit_max = ext4_num_base_meta_clusters(sb, block_group);
|
||||
if ((bit_max >> 3) >= bh->b_size)
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
for (bit = 0; bit < bit_max; bit++)
|
||||
ext4_set_bit(bit, bh->b_data);
|
||||
|
||||
@@ -610,7 +613,9 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries)
|
||||
|
||||
jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id);
|
||||
|
||||
jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal);
|
||||
smp_mb();
|
||||
if (EXT4_SB(sb)->s_mb_free_pending)
|
||||
jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,468 +0,0 @@
|
||||
/*
|
||||
* linux/fs/ext4/crypto_fname.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains functions for filename crypto management in ext4
|
||||
*
|
||||
* Written by Uday Savagaonkar, 2014.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/skcipher.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
|
||||
#include "ext4.h"
|
||||
#include "ext4_crypto.h"
|
||||
#include "xattr.h"
|
||||
|
||||
/**
|
||||
* ext4_dir_crypt_complete() -
|
||||
*/
|
||||
static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res)
|
||||
{
|
||||
struct ext4_completion_result *ecr = req->data;
|
||||
|
||||
if (res == -EINPROGRESS)
|
||||
return;
|
||||
ecr->res = res;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
bool ext4_valid_filenames_enc_mode(uint32_t mode)
|
||||
{
|
||||
return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS);
|
||||
}
|
||||
|
||||
static unsigned max_name_len(struct inode *inode)
|
||||
{
|
||||
return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize :
|
||||
EXT4_NAME_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_fname_encrypt() -
|
||||
*
|
||||
* This function encrypts the input filename, and returns the length of the
|
||||
* ciphertext. Errors are returned as negative numbers. We trust the caller to
|
||||
* allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int ext4_fname_encrypt(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
u32 ciphertext_len;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_EXT4_COMPLETION_RESULT(ecr);
|
||||
struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[EXT4_CRYPTO_BLOCK_SIZE];
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
int padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK);
|
||||
char *workbuf, buf[32], *alloc_buf = NULL;
|
||||
unsigned lim = max_name_len(inode);
|
||||
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ?
|
||||
EXT4_CRYPTO_BLOCK_SIZE : iname->len;
|
||||
ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding);
|
||||
ciphertext_len = (ciphertext_len > lim)
|
||||
? 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 = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(
|
||||
KERN_ERR "%s: crypto_request_alloc() failed\n", __func__);
|
||||
kfree(alloc_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
ext4_dir_crypt_complete, &ecr);
|
||||
|
||||
/* Copy the input */
|
||||
memcpy(workbuf, iname->name, iname->len);
|
||||
if (iname->len < ciphertext_len)
|
||||
memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create encryption request */
|
||||
sg_init_one(&src_sg, workbuf, ciphertext_len);
|
||||
sg_init_one(&dst_sg, oname->name, ciphertext_len);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
kfree(alloc_buf);
|
||||
skcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
printk_ratelimited(
|
||||
KERN_ERR "%s: Error (error code %d)\n", __func__, res);
|
||||
}
|
||||
oname->len = ciphertext_len;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* ext4_fname_decrypt()
|
||||
* This function decrypts the input filename, and returns
|
||||
* the length of the plaintext.
|
||||
* Errors are returned as negative numbers.
|
||||
* We trust the caller to allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int ext4_fname_decrypt(struct inode *inode,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
struct ext4_str tmp_in[2], tmp_out[1];
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_EXT4_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[EXT4_CRYPTO_BLOCK_SIZE];
|
||||
unsigned lim = max_name_len(inode);
|
||||
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
tmp_in[0].name = iname->name;
|
||||
tmp_in[0].len = iname->len;
|
||||
tmp_out[0].name = oname->name;
|
||||
|
||||
/* Allocate request */
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(
|
||||
KERN_ERR "%s: crypto_request_alloc() failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
ext4_dir_crypt_complete, &ecr);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create encryption request */
|
||||
sg_init_one(&src_sg, iname->name, iname->len);
|
||||
sg_init_one(&dst_sg, oname->name, oname->len);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
|
||||
res = crypto_skcipher_decrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
skcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
printk_ratelimited(
|
||||
KERN_ERR "%s: Error in ext4_fname_encrypt (error code %d)\n",
|
||||
__func__, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
oname->len = strnlen(oname->name, iname->len);
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
/**
|
||||
* ext4_fname_encode_digest() -
|
||||
*
|
||||
* Encodes the input digest using characters from the set [a-zA-Z0-9_+].
|
||||
* The encoded string is roughly 4/3 times the size of the input string.
|
||||
*/
|
||||
static int digest_encode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
ac += (((unsigned char) src[i]) << bits);
|
||||
bits += 8;
|
||||
do {
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
ac >>= 6;
|
||||
bits -= 6;
|
||||
} while (bits >= 6);
|
||||
i++;
|
||||
}
|
||||
if (bits)
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
static int digest_decode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
const char *p;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
p = strchr(lookup_table, src[i]);
|
||||
if (p == NULL || src[i] == 0)
|
||||
return -2;
|
||||
ac += (p - lookup_table) << bits;
|
||||
bits += 6;
|
||||
if (bits >= 8) {
|
||||
*cp++ = ac & 0xff;
|
||||
ac >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ac)
|
||||
return -1;
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_fname_crypto_round_up() -
|
||||
*
|
||||
* Return: The next multiple of block size
|
||||
*/
|
||||
u32 ext4_fname_crypto_round_up(u32 size, u32 blksize)
|
||||
{
|
||||
return ((size+blksize-1)/blksize)*blksize;
|
||||
}
|
||||
|
||||
unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||
{
|
||||
struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
|
||||
int padding = 32;
|
||||
|
||||
if (ci)
|
||||
padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK);
|
||||
if (ilen < EXT4_CRYPTO_BLOCK_SIZE)
|
||||
ilen = EXT4_CRYPTO_BLOCK_SIZE;
|
||||
return ext4_fname_crypto_round_up(ilen, padding);
|
||||
}
|
||||
|
||||
/*
|
||||
* ext4_fname_crypto_alloc_buffer() -
|
||||
*
|
||||
* Allocates an output buffer that is sufficient for the crypto operation
|
||||
* specified by the context and the direction.
|
||||
*/
|
||||
int ext4_fname_crypto_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct ext4_str *crypto_str)
|
||||
{
|
||||
unsigned int olen = ext4_fname_encrypted_size(inode, ilen);
|
||||
|
||||
crypto_str->len = olen;
|
||||
if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
|
||||
olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2;
|
||||
/* Allocated buffer can hold one more character to null-terminate the
|
||||
* string */
|
||||
crypto_str->name = kmalloc(olen+1, GFP_NOFS);
|
||||
if (!(crypto_str->name))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_fname_crypto_free_buffer() -
|
||||
*
|
||||
* Frees the buffer allocated for crypto operation.
|
||||
*/
|
||||
void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str)
|
||||
{
|
||||
if (!crypto_str)
|
||||
return;
|
||||
kfree(crypto_str->name);
|
||||
crypto_str->name = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_fname_disk_to_usr() - converts a filename from disk space to user space
|
||||
*/
|
||||
int _ext4_fname_disk_to_usr(struct inode *inode,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
char buf[24];
|
||||
int ret;
|
||||
|
||||
if (iname->len < 3) {
|
||||
/*Check for . and .. */
|
||||
if (iname->name[0] == '.' && iname->name[iname->len-1] == '.') {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len-1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
}
|
||||
if (iname->len < EXT4_CRYPTO_BLOCK_SIZE) {
|
||||
EXT4_ERROR_INODE(inode, "encrypted inode too small");
|
||||
return -EUCLEAN;
|
||||
}
|
||||
if (EXT4_I(inode)->i_crypt_info)
|
||||
return ext4_fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
ret = digest_encode(iname->name, iname->len, oname->name);
|
||||
oname->len = ret;
|
||||
return ret;
|
||||
}
|
||||
if (hinfo) {
|
||||
memcpy(buf, &hinfo->hash, 4);
|
||||
memcpy(buf+4, &hinfo->minor_hash, 4);
|
||||
} else
|
||||
memset(buf, 0, 8);
|
||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||
oname->name[0] = '_';
|
||||
ret = digest_encode(buf, 24, oname->name+1);
|
||||
oname->len = ret + 1;
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
int ext4_fname_disk_to_usr(struct inode *inode,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
struct ext4_str iname = {.name = (unsigned char *) de->name,
|
||||
.len = de->name_len };
|
||||
|
||||
return _ext4_fname_disk_to_usr(inode, hinfo, &iname, oname);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ext4_fname_usr_to_disk() - converts a filename from user space to disk space
|
||||
*/
|
||||
int ext4_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
int res;
|
||||
struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
|
||||
|
||||
if (iname->len < 3) {
|
||||
/*Check for . and .. */
|
||||
if (iname->name[0] == '.' &&
|
||||
iname->name[iname->len-1] == '.') {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len-1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
}
|
||||
if (ci) {
|
||||
res = ext4_fname_encrypt(inode, iname, oname);
|
||||
return res;
|
||||
}
|
||||
/* Without a proper key, a user is not allowed to modify the filenames
|
||||
* in a directory. Consequently, a user space name cannot be mapped to
|
||||
* a disk-space name */
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct ext4_filename *fname)
|
||||
{
|
||||
struct ext4_crypt_info *ci;
|
||||
int ret = 0, bigname = 0;
|
||||
|
||||
memset(fname, 0, sizeof(struct ext4_filename));
|
||||
fname->usr_fname = iname;
|
||||
|
||||
if (!ext4_encrypted_inode(dir) ||
|
||||
((iname->name[0] == '.') &&
|
||||
((iname->len == 1) ||
|
||||
((iname->name[1] == '.') && (iname->len == 2))))) {
|
||||
fname->disk_name.name = (unsigned char *) iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
ret = ext4_get_encryption_info(dir);
|
||||
if (ret)
|
||||
return ret;
|
||||
ci = EXT4_I(dir)->i_crypt_info;
|
||||
if (ci) {
|
||||
ret = ext4_fname_crypto_alloc_buffer(dir, iname->len,
|
||||
&fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = ext4_fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
return 0;
|
||||
}
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
|
||||
/* 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)))
|
||||
return -ENOENT;
|
||||
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
errout:
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
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;
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* linux/fs/ext4/crypto_key.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption key functions for ext4
|
||||
*
|
||||
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
|
||||
*/
|
||||
|
||||
#include <crypto/skcipher.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <uapi/linux/keyctl.h>
|
||||
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
{
|
||||
struct ext4_completion_result *ecr = req->data;
|
||||
|
||||
if (rc == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
ecr->res = rc;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_derive_key_aes() - Derive a key using AES-128-ECB
|
||||
* @deriving_key: Encryption key used for derivation.
|
||||
* @source_key: Source key to which to apply derivation.
|
||||
* @derived_key: Derived key.
|
||||
*
|
||||
* Return: Zero on success; non-zero otherwise.
|
||||
*/
|
||||
static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
|
||||
char source_key[EXT4_AES_256_XTS_KEY_SIZE],
|
||||
char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
|
||||
{
|
||||
int res = 0;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_EXT4_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
|
||||
|
||||
if (IS_ERR(tfm)) {
|
||||
res = PTR_ERR(tfm);
|
||||
tfm = NULL;
|
||||
goto out;
|
||||
}
|
||||
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
derive_crypt_complete, &ecr);
|
||||
res = crypto_skcipher_setkey(tfm, deriving_key,
|
||||
EXT4_AES_128_ECB_KEY_SIZE);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
sg_init_one(&src_sg, source_key, EXT4_AES_256_XTS_KEY_SIZE);
|
||||
sg_init_one(&dst_sg, derived_key, EXT4_AES_256_XTS_KEY_SIZE);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
EXT4_AES_256_XTS_KEY_SIZE, NULL);
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
|
||||
out:
|
||||
skcipher_request_free(req);
|
||||
crypto_free_skcipher(tfm);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ext4_free_crypt_info(struct ext4_crypt_info *ci)
|
||||
{
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
if (ci->ci_keyring_key)
|
||||
key_put(ci->ci_keyring_key);
|
||||
crypto_free_skcipher(ci->ci_ctfm);
|
||||
kmem_cache_free(ext4_crypt_info_cachep, ci);
|
||||
}
|
||||
|
||||
void ext4_free_encryption_info(struct inode *inode,
|
||||
struct ext4_crypt_info *ci)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
struct ext4_crypt_info *prev;
|
||||
|
||||
if (ci == NULL)
|
||||
ci = ACCESS_ONCE(ei->i_crypt_info);
|
||||
if (ci == NULL)
|
||||
return;
|
||||
prev = cmpxchg(&ei->i_crypt_info, ci, NULL);
|
||||
if (prev != ci)
|
||||
return;
|
||||
|
||||
ext4_free_crypt_info(ci);
|
||||
}
|
||||
|
||||
int _ext4_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
struct ext4_crypt_info *crypt_info;
|
||||
char full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE +
|
||||
(EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1];
|
||||
struct key *keyring_key = NULL;
|
||||
struct ext4_encryption_key *master_key;
|
||||
struct ext4_encryption_context ctx;
|
||||
const struct user_key_payload *ukp;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
struct crypto_skcipher *ctfm;
|
||||
const char *cipher_str;
|
||||
char raw_key[EXT4_MAX_KEY_SIZE];
|
||||
char mode;
|
||||
int res;
|
||||
|
||||
if (!ext4_read_workqueue) {
|
||||
res = ext4_init_crypto();
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
retry:
|
||||
crypt_info = ACCESS_ONCE(ei->i_crypt_info);
|
||||
if (crypt_info) {
|
||||
if (!crypt_info->ci_keyring_key ||
|
||||
key_validate(crypt_info->ci_keyring_key) == 0)
|
||||
return 0;
|
||||
ext4_free_encryption_info(inode, crypt_info);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx));
|
||||
if (res < 0) {
|
||||
if (!DUMMY_ENCRYPTION_ENABLED(sbi))
|
||||
return res;
|
||||
ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode =
|
||||
EXT4_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
} else if (res != sizeof(ctx))
|
||||
return -EINVAL;
|
||||
res = 0;
|
||||
|
||||
crypt_info = kmem_cache_alloc(ext4_crypt_info_cachep, GFP_KERNEL);
|
||||
if (!crypt_info)
|
||||
return -ENOMEM;
|
||||
|
||||
crypt_info->ci_flags = ctx.flags;
|
||||
crypt_info->ci_data_mode = ctx.contents_encryption_mode;
|
||||
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
|
||||
crypt_info->ci_ctfm = NULL;
|
||||
crypt_info->ci_keyring_key = NULL;
|
||||
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
|
||||
sizeof(crypt_info->ci_master_key));
|
||||
if (S_ISREG(inode->i_mode))
|
||||
mode = crypt_info->ci_data_mode;
|
||||
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
|
||||
mode = crypt_info->ci_filename_mode;
|
||||
else
|
||||
BUG();
|
||||
switch (mode) {
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_XTS:
|
||||
cipher_str = "xts(aes)";
|
||||
break;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
|
||||
cipher_str = "cts(cbc(aes))";
|
||||
break;
|
||||
default:
|
||||
printk_once(KERN_WARNING
|
||||
"ext4: unsupported key mode %d (ino %u)\n",
|
||||
mode, (unsigned) inode->i_ino);
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
if (DUMMY_ENCRYPTION_ENABLED(sbi)) {
|
||||
memset(raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE);
|
||||
goto got_key;
|
||||
}
|
||||
memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX,
|
||||
EXT4_KEY_DESC_PREFIX_SIZE);
|
||||
sprintf(full_key_descriptor + EXT4_KEY_DESC_PREFIX_SIZE,
|
||||
"%*phN", EXT4_KEY_DESCRIPTOR_SIZE,
|
||||
ctx.master_key_descriptor);
|
||||
full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE +
|
||||
(2 * EXT4_KEY_DESCRIPTOR_SIZE)] = '\0';
|
||||
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
|
||||
if (IS_ERR(keyring_key)) {
|
||||
res = PTR_ERR(keyring_key);
|
||||
keyring_key = NULL;
|
||||
goto out;
|
||||
}
|
||||
crypt_info->ci_keyring_key = keyring_key;
|
||||
if (keyring_key->type != &key_type_logon) {
|
||||
printk_once(KERN_WARNING
|
||||
"ext4: key type must be logon\n");
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
down_read(&keyring_key->sem);
|
||||
ukp = user_key_payload(keyring_key);
|
||||
if (ukp->datalen != sizeof(struct ext4_encryption_key)) {
|
||||
res = -EINVAL;
|
||||
up_read(&keyring_key->sem);
|
||||
goto out;
|
||||
}
|
||||
master_key = (struct ext4_encryption_key *)ukp->data;
|
||||
BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE !=
|
||||
EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
if (master_key->size != EXT4_AES_256_XTS_KEY_SIZE) {
|
||||
printk_once(KERN_WARNING
|
||||
"ext4: key size incorrect: %d\n",
|
||||
master_key->size);
|
||||
res = -ENOKEY;
|
||||
up_read(&keyring_key->sem);
|
||||
goto out;
|
||||
}
|
||||
res = ext4_derive_key_aes(ctx.nonce, master_key->raw,
|
||||
raw_key);
|
||||
up_read(&keyring_key->sem);
|
||||
if (res)
|
||||
goto out;
|
||||
got_key:
|
||||
ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
|
||||
if (!ctfm || IS_ERR(ctfm)) {
|
||||
res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
|
||||
printk(KERN_DEBUG
|
||||
"%s: error %d (inode %u) allocating crypto tfm\n",
|
||||
__func__, res, (unsigned) inode->i_ino);
|
||||
goto out;
|
||||
}
|
||||
crypt_info->ci_ctfm = ctfm;
|
||||
crypto_skcipher_clear_flags(ctfm, ~0);
|
||||
crypto_tfm_set_flags(crypto_skcipher_tfm(ctfm),
|
||||
CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
res = crypto_skcipher_setkey(ctfm, raw_key,
|
||||
ext4_encryption_key_size(mode));
|
||||
if (res)
|
||||
goto out;
|
||||
memzero_explicit(raw_key, sizeof(raw_key));
|
||||
if (cmpxchg(&ei->i_crypt_info, NULL, crypt_info) != NULL) {
|
||||
ext4_free_crypt_info(crypt_info);
|
||||
goto retry;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (res == -ENOKEY)
|
||||
res = 0;
|
||||
ext4_free_crypt_info(crypt_info);
|
||||
memzero_explicit(raw_key, sizeof(raw_key));
|
||||
return res;
|
||||
}
|
||||
|
||||
int ext4_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
|
||||
return (ei->i_crypt_info != NULL);
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* linux/fs/ext4/crypto_policy.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption policy functions for ext4
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
*/
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static int ext4_inode_has_encryption_context(struct inode *inode)
|
||||
{
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
|
||||
return (res > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether the policy is consistent with the encryption context
|
||||
* for the inode
|
||||
*/
|
||||
static int ext4_is_encryption_context_consistent_with_policy(
|
||||
struct inode *inode, const struct ext4_encryption_policy *policy)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return 0;
|
||||
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(ctx.flags ==
|
||||
policy->flags) &&
|
||||
(ctx.contents_encryption_mode ==
|
||||
policy->contents_encryption_mode) &&
|
||||
(ctx.filenames_encryption_mode ==
|
||||
policy->filenames_encryption_mode));
|
||||
}
|
||||
|
||||
static int ext4_create_encryption_context_from_policy(
|
||||
struct inode *inode, const struct ext4_encryption_policy *policy)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
handle_t *handle;
|
||||
int res, res2;
|
||||
|
||||
res = ext4_convert_inline_data(inode);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
if (!ext4_valid_contents_enc_mode(policy->contents_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid contents encryption mode %d\n", __func__,
|
||||
policy->contents_encryption_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!ext4_valid_filenames_enc_mode(policy->filenames_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid filenames encryption mode %d\n", __func__,
|
||||
policy->filenames_encryption_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (policy->flags & ~EXT4_POLICY_FLAGS_VALID)
|
||||
return -EINVAL;
|
||||
ctx.contents_encryption_mode = policy->contents_encryption_mode;
|
||||
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
|
||||
ctx.flags = policy->flags;
|
||||
BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
handle = ext4_journal_start(inode, EXT4_HT_MISC,
|
||||
ext4_jbd2_credits_xattr(inode));
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), 0);
|
||||
if (!res) {
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
res = ext4_mark_inode_dirty(handle, inode);
|
||||
if (res)
|
||||
EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
|
||||
}
|
||||
res2 = ext4_journal_stop(handle);
|
||||
if (!res)
|
||||
res = res2;
|
||||
return res;
|
||||
}
|
||||
|
||||
int ext4_process_policy(const struct ext4_encryption_policy *policy,
|
||||
struct inode *inode)
|
||||
{
|
||||
if (policy->version != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ext4_inode_has_encryption_context(inode)) {
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return -EINVAL;
|
||||
if (!ext4_empty_dir(inode))
|
||||
return -ENOTEMPTY;
|
||||
return ext4_create_encryption_context_from_policy(inode,
|
||||
policy);
|
||||
}
|
||||
|
||||
if (ext4_is_encryption_context_consistent_with_policy(inode, policy))
|
||||
return 0;
|
||||
|
||||
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return -ENOENT;
|
||||
if (ctx.format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1)
|
||||
return -EINVAL;
|
||||
policy->version = 0;
|
||||
policy->contents_encryption_mode = ctx.contents_encryption_mode;
|
||||
policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
|
||||
policy->flags = ctx.flags;
|
||||
memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
|
||||
struct inode *child)
|
||||
{
|
||||
struct ext4_crypt_info *parent_ci, *child_ci;
|
||||
int res;
|
||||
|
||||
if ((parent == NULL) || (child == NULL)) {
|
||||
pr_err("parent %p child %p\n", parent, child);
|
||||
WARN_ON(1); /* Should never happen */
|
||||
return 0;
|
||||
}
|
||||
/* no restrictions if the parent directory is not encrypted */
|
||||
if (!ext4_encrypted_inode(parent))
|
||||
return 1;
|
||||
/* if the child directory is not encrypted, this is always a problem */
|
||||
if (!ext4_encrypted_inode(child))
|
||||
return 0;
|
||||
res = ext4_get_encryption_info(parent);
|
||||
if (res)
|
||||
return 0;
|
||||
res = ext4_get_encryption_info(child);
|
||||
if (res)
|
||||
return 0;
|
||||
parent_ci = EXT4_I(parent)->i_crypt_info;
|
||||
child_ci = EXT4_I(child)->i_crypt_info;
|
||||
if (!parent_ci && !child_ci)
|
||||
return 1;
|
||||
if (!parent_ci || !child_ci)
|
||||
return 0;
|
||||
|
||||
return (memcmp(parent_ci->ci_master_key,
|
||||
child_ci->ci_master_key,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
|
||||
(parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
|
||||
(parent_ci->ci_flags == child_ci->ci_flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_inherit_context() - Sets a child context from its parent
|
||||
* @parent: Parent inode from which the context is inherited.
|
||||
* @child: Child inode that inherits the context from @parent.
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise
|
||||
*/
|
||||
int ext4_inherit_context(struct inode *parent, struct inode *child)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
struct ext4_crypt_info *ci;
|
||||
int res;
|
||||
|
||||
res = ext4_get_encryption_info(parent);
|
||||
if (res < 0)
|
||||
return res;
|
||||
ci = EXT4_I(parent)->i_crypt_info;
|
||||
if (ci == NULL)
|
||||
return -ENOKEY;
|
||||
|
||||
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
if (DUMMY_ENCRYPTION_ENABLED(EXT4_SB(parent->i_sb))) {
|
||||
ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode =
|
||||
EXT4_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
memset(ctx.master_key_descriptor, 0x42,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
res = 0;
|
||||
} else {
|
||||
ctx.contents_encryption_mode = ci->ci_data_mode;
|
||||
ctx.filenames_encryption_mode = ci->ci_filename_mode;
|
||||
ctx.flags = ci->ci_flags;
|
||||
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
}
|
||||
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), 0);
|
||||
if (!res) {
|
||||
ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
|
||||
ext4_clear_inode_state(child, EXT4_STATE_MAY_INLINE_DATA);
|
||||
res = ext4_get_encryption_info(child);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
+14
-12
@@ -109,10 +109,10 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh = NULL;
|
||||
int dir_has_error = 0;
|
||||
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
|
||||
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
|
||||
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
err = ext4_get_encryption_info(inode);
|
||||
err = fscrypt_get_encryption_info(inode);
|
||||
if (err && err != -ENOKEY)
|
||||
return err;
|
||||
}
|
||||
@@ -139,8 +139,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
}
|
||||
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
err = ext4_fname_crypto_alloc_buffer(inode, EXT4_NAME_LEN,
|
||||
&fname_crypto_str);
|
||||
err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@@ -253,16 +252,19 @@ 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;
|
||||
int save_len = fstr.len;
|
||||
struct fscrypt_str de_name =
|
||||
FSTR_INIT(de->name,
|
||||
de->name_len);
|
||||
|
||||
/* Directory is encrypted */
|
||||
err = ext4_fname_disk_to_usr(inode,
|
||||
NULL, de, &fname_crypto_str);
|
||||
fname_crypto_str.len = save_len;
|
||||
err = fscrypt_fname_disk_to_usr(inode,
|
||||
0, 0, &de_name, &fstr);
|
||||
fstr.len = save_len;
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
if (!dir_emit(ctx,
|
||||
fname_crypto_str.name, err,
|
||||
fstr.name, err,
|
||||
le32_to_cpu(de->inode),
|
||||
get_dtype(sb, de->file_type)))
|
||||
goto done;
|
||||
@@ -281,7 +283,7 @@ done:
|
||||
err = 0;
|
||||
errout:
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
ext4_fname_crypto_free_buffer(&fname_crypto_str);
|
||||
fscrypt_fname_free_buffer(&fstr);
|
||||
#endif
|
||||
brelse(bh);
|
||||
return err;
|
||||
@@ -432,7 +434,7 @@ void ext4_htree_free_dir_info(struct dir_private_info *p)
|
||||
int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
||||
__u32 minor_hash,
|
||||
struct ext4_dir_entry_2 *dirent,
|
||||
struct ext4_str *ent_name)
|
||||
struct fscrypt_str *ent_name)
|
||||
{
|
||||
struct rb_node **p, *parent = NULL;
|
||||
struct fname *fname, *new_fn;
|
||||
@@ -609,7 +611,7 @@ finished:
|
||||
static int ext4_dir_open(struct inode * inode, struct file * filp)
|
||||
{
|
||||
if (ext4_encrypted_inode(inode))
|
||||
return ext4_get_encryption_info(inode) ? -EACCES : 0;
|
||||
return fscrypt_get_encryption_info(inode) ? -EACCES : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+81
-140
@@ -32,6 +32,7 @@
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/percpu-rwsem.h>
|
||||
#ifdef __KERNEL__
|
||||
@@ -608,15 +609,6 @@ enum {
|
||||
#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010
|
||||
#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define EXT4_ENCRYPTION_MODE_INVALID 0
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
|
||||
#include "ext4_crypto.h"
|
||||
|
||||
/*
|
||||
* ioctl commands
|
||||
*/
|
||||
@@ -638,9 +630,9 @@ enum {
|
||||
#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
|
||||
#define EXT4_IOC_SWAP_BOOT _IO('f', 17)
|
||||
#define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18)
|
||||
#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
|
||||
#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
|
||||
#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
|
||||
#define EXT4_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
|
||||
#define EXT4_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT
|
||||
#define EXT4_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
|
||||
|
||||
#ifndef FS_IOC_FSGETXATTR
|
||||
/* Until the uapi changes get merged for project quota... */
|
||||
@@ -1082,10 +1074,6 @@ struct ext4_inode_info {
|
||||
/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
|
||||
__u32 i_csum_seed;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
/* Encryption params */
|
||||
struct ext4_crypt_info *i_crypt_info;
|
||||
#endif
|
||||
kprojid_t i_projid;
|
||||
};
|
||||
|
||||
@@ -1344,6 +1332,11 @@ struct ext4_super_block {
|
||||
/* Number of quota types we support */
|
||||
#define EXT4_MAXQUOTAS 3
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
#define EXT4_KEY_DESC_PREFIX "ext4:"
|
||||
#define EXT4_KEY_DESC_PREFIX_SIZE 5
|
||||
#endif
|
||||
|
||||
/*
|
||||
* fourth extended-fs super-block data in memory
|
||||
*/
|
||||
@@ -1430,6 +1423,7 @@ struct ext4_sb_info {
|
||||
unsigned short *s_mb_offsets;
|
||||
unsigned int *s_mb_maxs;
|
||||
unsigned int s_group_info_size;
|
||||
unsigned int s_mb_free_pending;
|
||||
|
||||
/* tunables */
|
||||
unsigned long s_stripe;
|
||||
@@ -1512,6 +1506,12 @@ struct ext4_sb_info {
|
||||
|
||||
/* Barrier between changing inodes' journal flags and writepages ops. */
|
||||
struct percpu_rw_semaphore s_journal_flag_rwsem;
|
||||
|
||||
/* Encryption support */
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
u8 key_prefix[EXT4_KEY_DESC_PREFIX_SIZE];
|
||||
u8 key_prefix_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||
@@ -1610,15 +1610,6 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
|
||||
/*
|
||||
* Returns true if the inode is inode is encrypted
|
||||
*/
|
||||
static inline int ext4_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
|
||||
|
||||
/*
|
||||
@@ -2082,10 +2073,10 @@ struct dx_hash_info
|
||||
|
||||
struct ext4_filename {
|
||||
const struct qstr *usr_fname;
|
||||
struct ext4_str disk_name;
|
||||
struct fscrypt_str disk_name;
|
||||
struct dx_hash_info hinfo;
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
struct ext4_str crypto_buf;
|
||||
struct fscrypt_str crypto_buf;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -2296,81 +2287,51 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
|
||||
struct ext4_group_desc *gdp);
|
||||
ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
||||
|
||||
/* crypto_policy.c */
|
||||
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
|
||||
struct inode *child);
|
||||
int ext4_inherit_context(struct inode *parent, struct inode *child);
|
||||
void ext4_to_hex(char *dst, char *src, size_t src_size);
|
||||
int ext4_process_policy(const struct ext4_encryption_policy *policy,
|
||||
struct inode *inode);
|
||||
int ext4_get_policy(struct inode *inode,
|
||||
struct ext4_encryption_policy *policy);
|
||||
|
||||
/* crypto.c */
|
||||
extern struct kmem_cache *ext4_crypt_info_cachep;
|
||||
bool ext4_valid_contents_enc_mode(uint32_t mode);
|
||||
uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size);
|
||||
extern struct workqueue_struct *ext4_read_workqueue;
|
||||
struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode,
|
||||
gfp_t gfp_flags);
|
||||
void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx);
|
||||
void ext4_restore_control_page(struct page *data_page);
|
||||
struct page *ext4_encrypt(struct inode *inode,
|
||||
struct page *plaintext_page,
|
||||
gfp_t gfp_flags);
|
||||
int ext4_decrypt(struct page *page);
|
||||
int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk,
|
||||
ext4_fsblk_t pblk, ext4_lblk_t len);
|
||||
extern const struct dentry_operations ext4_encrypted_d_ops;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int ext4_init_crypto(void);
|
||||
void ext4_exit_crypto(void);
|
||||
static inline int ext4_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
return ext4_has_feature_encrypt(sb);
|
||||
}
|
||||
#else
|
||||
static inline int ext4_init_crypto(void) { return 0; }
|
||||
static inline void ext4_exit_crypto(void) { }
|
||||
static inline int ext4_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* crypto_fname.c */
|
||||
bool ext4_valid_filenames_enc_mode(uint32_t mode);
|
||||
u32 ext4_fname_crypto_round_up(u32 size, u32 blksize);
|
||||
unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen);
|
||||
int ext4_fname_crypto_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct ext4_str *crypto_str);
|
||||
int _ext4_fname_disk_to_usr(struct inode *inode,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_disk_to_usr(struct inode *inode,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct ext4_str *oname);
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str);
|
||||
int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct ext4_filename *fname);
|
||||
void ext4_fname_free_filename(struct ext4_filename *fname);
|
||||
#else
|
||||
static inline
|
||||
int ext4_setup_fname_crypto(struct inode *inode)
|
||||
static inline bool ext4_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
}
|
||||
static inline void ext4_fname_crypto_free_buffer(struct ext4_str *p) { }
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct ext4_filename *fname)
|
||||
const struct qstr *iname,
|
||||
int lookup, struct ext4_filename *fname)
|
||||
{
|
||||
struct fscrypt_name name;
|
||||
int err;
|
||||
|
||||
memset(fname, 0, sizeof(struct ext4_filename));
|
||||
|
||||
err = fscrypt_setup_filename(dir, iname, lookup, &name);
|
||||
|
||||
fname->usr_fname = name.usr_fname;
|
||||
fname->disk_name = name.disk_name;
|
||||
fname->hinfo.hash = name.hash;
|
||||
fname->hinfo.minor_hash = name.minor_hash;
|
||||
fname->crypto_buf = name.crypto_buf;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void ext4_fname_free_filename(struct ext4_filename *fname)
|
||||
{
|
||||
struct fscrypt_name name;
|
||||
|
||||
name.crypto_buf = fname->crypto_buf;
|
||||
fscrypt_free_filename(&name);
|
||||
|
||||
fname->crypto_buf.name = NULL;
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
}
|
||||
#else
|
||||
static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct ext4_filename *fname)
|
||||
{
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *) iname->name;
|
||||
@@ -2378,51 +2339,31 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
return 0;
|
||||
}
|
||||
static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
|
||||
|
||||
#define fscrypt_set_d_op(i)
|
||||
#define fscrypt_get_ctx fscrypt_notsupp_get_ctx
|
||||
#define fscrypt_release_ctx fscrypt_notsupp_release_ctx
|
||||
#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page
|
||||
#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page
|
||||
#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages
|
||||
#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page
|
||||
#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page
|
||||
#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range
|
||||
#define fscrypt_process_policy fscrypt_notsupp_process_policy
|
||||
#define fscrypt_get_policy fscrypt_notsupp_get_policy
|
||||
#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context
|
||||
#define fscrypt_inherit_context fscrypt_notsupp_inherit_context
|
||||
#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info
|
||||
#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info
|
||||
#define fscrypt_setup_filename fscrypt_notsupp_setup_filename
|
||||
#define fscrypt_free_filename fscrypt_notsupp_free_filename
|
||||
#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size
|
||||
#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer
|
||||
#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer
|
||||
#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr
|
||||
#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
|
||||
#endif
|
||||
|
||||
|
||||
/* crypto_key.c */
|
||||
void ext4_free_crypt_info(struct ext4_crypt_info *ci);
|
||||
void ext4_free_encryption_info(struct inode *inode, struct ext4_crypt_info *ci);
|
||||
int _ext4_get_encryption_info(struct inode *inode);
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int ext4_has_encryption_key(struct inode *inode);
|
||||
|
||||
static inline int ext4_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
|
||||
|
||||
if (!ci ||
|
||||
(ci->ci_keyring_key &&
|
||||
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD)))))
|
||||
return _ext4_get_encryption_info(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
|
||||
{
|
||||
return EXT4_I(inode)->i_crypt_info;
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int ext4_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int ext4_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* dir.c */
|
||||
extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
|
||||
struct file *,
|
||||
@@ -2435,7 +2376,7 @@ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
|
||||
extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
||||
__u32 minor_hash,
|
||||
struct ext4_dir_entry_2 *dirent,
|
||||
struct ext4_str *ent_name);
|
||||
struct fscrypt_str *ent_name);
|
||||
extern void ext4_htree_free_dir_info(struct dir_private_info *p);
|
||||
extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
struct buffer_head *bh,
|
||||
@@ -2623,7 +2564,7 @@ extern int ext4_generic_delete_entry(handle_t *handle,
|
||||
void *entry_buf,
|
||||
int buf_size,
|
||||
int csum_size);
|
||||
extern int ext4_empty_dir(struct inode *inode);
|
||||
extern bool ext4_empty_dir(struct inode *inode);
|
||||
|
||||
/* resize.c */
|
||||
extern int ext4_group_add(struct super_block *sb,
|
||||
@@ -3105,7 +3046,7 @@ extern int ext4_delete_inline_entry(handle_t *handle,
|
||||
struct ext4_dir_entry_2 *de_del,
|
||||
struct buffer_head *bh,
|
||||
int *has_inline_data);
|
||||
extern int empty_inline_dir(struct inode *dir, int *has_inline_data);
|
||||
extern bool empty_inline_dir(struct inode *dir, int *has_inline_data);
|
||||
extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
|
||||
struct ext4_dir_entry_2 **parent_de,
|
||||
int *retval);
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* linux/fs/ext4/ext4_crypto.h
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption header content for ext4
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _EXT4_CRYPTO_H
|
||||
#define _EXT4_CRYPTO_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define EXT4_KEY_DESCRIPTOR_SIZE 8
|
||||
|
||||
/* Policy provided via an ioctl on the topmost directory */
|
||||
struct ext4_encryption_policy {
|
||||
char version;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char flags;
|
||||
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
#define EXT4_POLICY_FLAGS_PAD_4 0x00
|
||||
#define EXT4_POLICY_FLAGS_PAD_8 0x01
|
||||
#define EXT4_POLICY_FLAGS_PAD_16 0x02
|
||||
#define EXT4_POLICY_FLAGS_PAD_32 0x03
|
||||
#define EXT4_POLICY_FLAGS_PAD_MASK 0x03
|
||||
#define EXT4_POLICY_FLAGS_VALID 0x03
|
||||
|
||||
/**
|
||||
* Encryption context for inode
|
||||
*
|
||||
* Protector format:
|
||||
* 1 byte: Protector format (1 = this version)
|
||||
* 1 byte: File contents encryption mode
|
||||
* 1 byte: File names encryption mode
|
||||
* 1 byte: Reserved
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct ext4_encryption_context {
|
||||
char format;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char flags;
|
||||
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Encryption parameters */
|
||||
#define EXT4_XTS_TWEAK_SIZE 16
|
||||
#define EXT4_AES_128_ECB_KEY_SIZE 16
|
||||
#define EXT4_AES_256_GCM_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CBC_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CTS_KEY_SIZE 32
|
||||
#define EXT4_AES_256_XTS_KEY_SIZE 64
|
||||
#define EXT4_MAX_KEY_SIZE 64
|
||||
|
||||
#define EXT4_KEY_DESC_PREFIX "ext4:"
|
||||
#define EXT4_KEY_DESC_PREFIX_SIZE 5
|
||||
|
||||
/* This is passed in from userspace into the kernel keyring */
|
||||
struct ext4_encryption_key {
|
||||
__u32 mode;
|
||||
char raw[EXT4_MAX_KEY_SIZE];
|
||||
__u32 size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct ext4_crypt_info {
|
||||
char ci_data_mode;
|
||||
char ci_filename_mode;
|
||||
char ci_flags;
|
||||
struct crypto_skcipher *ci_ctfm;
|
||||
struct key *ci_keyring_key;
|
||||
char ci_master_key[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
};
|
||||
|
||||
#define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define EXT4_WRITE_PATH_FL 0x00000002
|
||||
|
||||
struct ext4_crypto_ctx {
|
||||
union {
|
||||
struct {
|
||||
struct page *bounce_page; /* Ciphertext page */
|
||||
struct page *control_page; /* Original page */
|
||||
} w;
|
||||
struct {
|
||||
struct bio *bio;
|
||||
struct work_struct work;
|
||||
} r;
|
||||
struct list_head free_list; /* Free list */
|
||||
};
|
||||
char flags; /* Flags */
|
||||
char mode; /* Encryption mode for tfm */
|
||||
};
|
||||
|
||||
struct ext4_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_EXT4_COMPLETION_RESULT(ecr) \
|
||||
struct ext4_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER((ecr).completion), 0 }
|
||||
|
||||
static inline int ext4_encryption_key_size(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_XTS:
|
||||
return EXT4_AES_256_XTS_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_GCM:
|
||||
return EXT4_AES_256_GCM_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_CBC:
|
||||
return EXT4_AES_256_CBC_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
|
||||
return EXT4_AES_256_CTS_KEY_SIZE;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define EXT4_FNAME_NUM_SCATTER_ENTRIES 4
|
||||
#define EXT4_CRYPTO_BLOCK_SIZE 16
|
||||
#define EXT4_FNAME_CRYPTO_DIGEST_SIZE 32
|
||||
|
||||
struct ext4_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
*/
|
||||
struct ext4_encrypted_symlink_data {
|
||||
__le16 len;
|
||||
char encrypted_path[1];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* This function is used to calculate the disk space required to
|
||||
* store a filename of length l in encrypted symlink format.
|
||||
*/
|
||||
static inline u32 encrypted_symlink_data_len(u32 l)
|
||||
{
|
||||
if (l < EXT4_CRYPTO_BLOCK_SIZE)
|
||||
l = EXT4_CRYPTO_BLOCK_SIZE;
|
||||
return (l + sizeof(struct ext4_encrypted_symlink_data) - 1);
|
||||
}
|
||||
|
||||
#endif /* _EXT4_CRYPTO_H */
|
||||
+9
-1
@@ -175,6 +175,13 @@ struct ext4_journal_cb_entry {
|
||||
* There is no guaranteed calling order of multiple registered callbacks on
|
||||
* the same transaction.
|
||||
*/
|
||||
static inline void _ext4_journal_callback_add(handle_t *handle,
|
||||
struct ext4_journal_cb_entry *jce)
|
||||
{
|
||||
/* Add the jce to transaction's private list */
|
||||
list_add_tail(&jce->jce_list, &handle->h_transaction->t_private_list);
|
||||
}
|
||||
|
||||
static inline void ext4_journal_callback_add(handle_t *handle,
|
||||
void (*func)(struct super_block *sb,
|
||||
struct ext4_journal_cb_entry *jce,
|
||||
@@ -187,10 +194,11 @@ static inline void ext4_journal_callback_add(handle_t *handle,
|
||||
/* Add the jce to transaction's private list */
|
||||
jce->jce_func = func;
|
||||
spin_lock(&sbi->s_md_lock);
|
||||
list_add_tail(&jce->jce_list, &handle->h_transaction->t_private_list);
|
||||
_ext4_journal_callback_add(handle, jce);
|
||||
spin_unlock(&sbi->s_md_lock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ext4_journal_callback_del: delete a registered callback
|
||||
* @handle: active journal transaction handle on which callback was registered
|
||||
|
||||
+10
-2
@@ -381,9 +381,13 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
|
||||
ext4_fsblk_t block = ext4_ext_pblock(ext);
|
||||
int len = ext4_ext_get_actual_len(ext);
|
||||
ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
|
||||
ext4_lblk_t last = lblock + len - 1;
|
||||
|
||||
if (len == 0 || lblock > last)
|
||||
/*
|
||||
* We allow neither:
|
||||
* - zero length
|
||||
* - overflow/wrap-around
|
||||
*/
|
||||
if (lblock + len <= lblock)
|
||||
return 0;
|
||||
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
|
||||
}
|
||||
@@ -474,6 +478,10 @@ static int __ext4_ext_check(const char *function, unsigned int line,
|
||||
error_msg = "invalid extent entries";
|
||||
goto corrupted;
|
||||
}
|
||||
if (unlikely(depth > 32)) {
|
||||
error_msg = "too large eh_depth";
|
||||
goto corrupted;
|
||||
}
|
||||
/* Verify checksum on non-root extent tree nodes */
|
||||
if (ext_depth(inode) != depth &&
|
||||
!ext4_extent_block_csum_verify(inode, eh)) {
|
||||
|
||||
+5
-5
@@ -303,10 +303,10 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
int err = ext4_get_encryption_info(inode);
|
||||
int err = fscrypt_get_encryption_info(inode);
|
||||
if (err)
|
||||
return 0;
|
||||
if (ext4_encryption_info(inode) == NULL)
|
||||
if (!fscrypt_has_encryption_key(inode))
|
||||
return -ENOKEY;
|
||||
}
|
||||
file_accessed(file);
|
||||
@@ -362,16 +362,16 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
|
||||
}
|
||||
}
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
ret = ext4_get_encryption_info(inode);
|
||||
ret = fscrypt_get_encryption_info(inode);
|
||||
if (ret)
|
||||
return -EACCES;
|
||||
if (ext4_encryption_info(inode) == NULL)
|
||||
if (!fscrypt_has_encryption_key(inode))
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
dir = dget_parent(file_dentry(filp));
|
||||
if (ext4_encrypted_inode(d_inode(dir)) &&
|
||||
!ext4_is_child_context_consistent_with_parent(d_inode(dir), inode)) {
|
||||
!fscrypt_has_permitted_context(d_inode(dir), inode)) {
|
||||
ext4_warning(inode->i_sb,
|
||||
"Inconsistent encryption contexts: %lu/%lu",
|
||||
(unsigned long) d_inode(dir)->i_ino,
|
||||
|
||||
+4
-1
@@ -106,9 +106,11 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
}
|
||||
|
||||
if (!journal) {
|
||||
ret = generic_file_fsync(file, start, end, datasync);
|
||||
ret = __generic_file_fsync(file, start, end, datasync);
|
||||
if (!ret && !hlist_empty(&inode->i_dentry))
|
||||
ret = ext4_sync_parent(inode);
|
||||
if (test_opt(inode->i_sb, BARRIER))
|
||||
goto issue_flush;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -140,6 +142,7 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
needs_barrier = true;
|
||||
ret = jbd2_complete_transaction(journal, commit_tid);
|
||||
if (needs_barrier) {
|
||||
issue_flush:
|
||||
err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
|
||||
if (!ret)
|
||||
ret = err;
|
||||
|
||||
+4
-3
@@ -767,10 +767,10 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
|
||||
if ((ext4_encrypted_inode(dir) ||
|
||||
DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
|
||||
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
|
||||
err = ext4_get_encryption_info(dir);
|
||||
err = fscrypt_get_encryption_info(dir);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
if (ext4_encryption_info(dir) == NULL)
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return ERR_PTR(-EPERM);
|
||||
if (!handle)
|
||||
nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb);
|
||||
@@ -1115,7 +1115,8 @@ got:
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
err = ext4_inherit_context(dir, inode);
|
||||
/* give pointer to avoid set_context with journal ops. */
|
||||
err = fscrypt_inherit_context(dir, inode, &encrypt, true);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user