diff mbox

[06/22] ext4 crypto: add encryption policy checking

Message ID 1428012659-12709-7-git-send-email-tytso@mit.edu
State Superseded, archived
Headers show

Commit Message

Theodore Ts'o April 2, 2015, 10:10 p.m. UTC
From: Michael Halcrow <mhalcrow@google.com>

The ext4_crypto.h header will get fleshed out as later patches in this
patchset add functionality.

Change-Id: I550d197184af04ed27e4c3abb759ca188a3f0de0
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
---
 fs/ext4/Makefile        |   1 +
 fs/ext4/crypto_policy.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/ext4.h          |   2 +
 fs/ext4/ext4_crypto.h   |  54 +++++++++++++++++
 4 files changed, 212 insertions(+)
 create mode 100644 fs/ext4/crypto_policy.c
 create mode 100644 fs/ext4/ext4_crypto.h

Comments

Andreas Dilger April 6, 2015, 9:31 p.m. UTC | #1
On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> The ext4_crypto.h header will get fleshed out as later patches in this
> patchset add functionality.
> 
> Change-Id: I550d197184af04ed27e4c3abb759ca188a3f0de0
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
> ---
> fs/ext4/Makefile        |   1 +
> fs/ext4/crypto_policy.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/ext4.h          |   2 +
> fs/ext4/ext4_crypto.h   |  54 +++++++++++++++++
> 4 files changed, 212 insertions(+)
> create mode 100644 fs/ext4/crypto_policy.c
> create mode 100644 fs/ext4/ext4_crypto.h
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index cd6f50f..3886ee4 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -12,3 +12,4 @@ 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
> diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
> new file mode 100644
> index 0000000..5cb4e74
> --- /dev/null
> +++ b/fs/ext4/crypto_policy.c
> @@ -0,0 +1,155 @@
> +/*
> + * linux/fs/ext4/crypto_policy.c
> + *
> + * 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.h"
> +#include "xattr.h"
> +
> +/**
> + * ext4_to_hex() - Converts to hexadecimal characters
> + * @dst: Buffer to take hex character representation of contents of
> + *       src. Must be at least of size (src_size * 2).
> + * @src: Buffer to be converted to a hex string respresentation.
> + * @src_size: Number of bytes to convert.
> + */
> +void ext4_to_hex(char *dst, char *src, size_t src_size)
> +{
> +	int x;
> +
> +	for (x = 0; x < src_size; x++)
> +		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
> +}

I think there is already some code in printk() to handle this?  Looking
at vsnprintf->hex_string() it looks like "%*ph" would print out up to 64
bytes as hex.

Cheers, Andreas

> +
> +/**
> + *
> + */
> +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);
> +}
> +
> +/**
> + * ext4_is_encryption_context_consistent_with_policy() - Checks whether the policy is consistent with the encryption context for the inode
> + * @inode:  ...
> + * @policy: ...
> + *
> + * Return ...
> + */
> +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.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;
> +	int res = 0;
> +
> +	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
> +	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
> +	       EXT4_KEY_DESCRIPTOR_SIZE);
> +	ctx.contents_encryption_mode = policy->contents_encryption_mode;
> +	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
> +	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
> +	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
> +
> +	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);
> +	return res;
> +}
> +
> +int ext4_process_policy(const struct ext4_encryption_policy *policy,
> +			struct inode *inode)
> +{
> +	int res = 0;
> +
> +	if (!ext4_inode_has_encryption_context(inode)) {
> +		res = ext4_create_encryption_context_from_policy(inode, policy);
> +		goto out;
> +	}
> +
> +	if (!ext4_is_encryption_context_consistent_with_policy(inode, policy)) {
> +		printk(KERN_WARNING
> +		       "%s: Policy inconsistent with encryption context\n",
> +		       __func__);
> +		res = -EINVAL;
> +	}
> +out:
> +	return res;
> +}
> +
> +int ext4_is_child_context_consistent_with_parent(struct inode *parent,
> +						 struct inode *child)
> +{
> +	struct ext4_encryption_context parent_ctx, child_ctx;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &parent_ctx, sizeof(parent_ctx));
> +
> +	if (res != sizeof(parent_ctx))
> +		return 0;
> +	res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +			     &child_ctx, sizeof(child_ctx));
> +	if (res != sizeof(child_ctx))
> +		return 0;
> +	return (memcmp(parent_ctx.master_key_descriptor,
> +		       child_ctx.master_key_descriptor,
> +		       EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
> +		(parent_ctx.contents_encryption_mode ==
> +		 child_ctx.contents_encryption_mode) &&
> +		(parent_ctx.filenames_encryption_mode ==
> +		 child_ctx.filenames_encryption_mode));
> +}
> +
> +/**
> + * 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;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &ctx, sizeof(ctx));
> +
> +	if (res != sizeof(ctx))
> +		return -ENOENT;
> +
> +	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);
> +	return res;
> +}
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 2f3808e..fd2f3dd 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -588,6 +588,8 @@ enum {
> #define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
> #define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
> 
> +#include "ext4_crypto.h"
> +
> /*
>  * ioctl commands
>  */
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> new file mode 100644
> index 0000000..984ff38
> --- /dev/null
> +++ b/fs/ext4/ext4_crypto.h
> @@ -0,0 +1,54 @@
> +/*
> + * linux/fs/ext4/ext4_crypto.h
> + *
> + * 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 master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +} __attribute__((__packed__));
> +
> +#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0
> +#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
> +
> +/**
> + * Encryption context for inode
> + *
> + * Protector format:
> + *  1 byte: Protector format (0 = 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 reserved;
> +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +	char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
> +} __attribute__((__packed__));
> +
> +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);
> +
> +#endif	/* _EXT4_CRYPTO_H */
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas





--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andreas Dilger April 8, 2015, 6:07 p.m. UTC | #2
On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> The ext4_crypto.h header will get fleshed out as later patches in this
> patchset add functionality.
> 
> Change-Id: I550d197184af04ed27e4c3abb759ca188a3f0de0
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
> ---
> fs/ext4/Makefile        |   1 +
> fs/ext4/crypto_policy.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/ext4.h          |   2 +
> fs/ext4/ext4_crypto.h   |  54 +++++++++++++++++
> 4 files changed, 212 insertions(+)
> create mode 100644 fs/ext4/crypto_policy.c
> create mode 100644 fs/ext4/ext4_crypto.h
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index cd6f50f..3886ee4 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -12,3 +12,4 @@ 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
> diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
> new file mode 100644
> index 0000000..5cb4e74
> --- /dev/null
> +++ b/fs/ext4/crypto_policy.c
> @@ -0,0 +1,155 @@
> +/*
> + * linux/fs/ext4/crypto_policy.c
> + *
> + * 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.h"
> +#include "xattr.h"
> +
> +/**
> + * ext4_to_hex() - Converts to hexadecimal characters
> + * @dst: Buffer to take hex character representation of contents of
> + *       src. Must be at least of size (src_size * 2).
> + * @src: Buffer to be converted to a hex string respresentation.
> + * @src_size: Number of bytes to convert.
> + */
> +void ext4_to_hex(char *dst, char *src, size_t src_size)
> +{
> +	int x;
> +
> +	for (x = 0; x < src_size; x++)
> +		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
> +}
> +
> +/**
> + *
> + */
> +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);
> +}
> +
> +/**
> + * ext4_is_encryption_context_consistent_with_policy() - Checks whether the policy is consistent with the encryption context for the inode
> + * @inode:  ...
> + * @policy: ...
> + *
> + * Return ...
> + */
> +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.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;
> +	int res = 0;
> +
> +	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
> +	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
> +	       EXT4_KEY_DESCRIPTOR_SIZE);
> +	ctx.contents_encryption_mode = policy->contents_encryption_mode;
> +	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
> +	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
> +	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
> +
> +	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);
> +	return res;
> +}
> +
> +int ext4_process_policy(const struct ext4_encryption_policy *policy,
> +			struct inode *inode)
> +{
> +	int res = 0;
> +
> +	if (!ext4_inode_has_encryption_context(inode)) {
> +		res = ext4_create_encryption_context_from_policy(inode, policy);
> +		goto out;
> +	}
> +
> +	if (!ext4_is_encryption_context_consistent_with_policy(inode, policy)) {
> +		printk(KERN_WARNING
> +		       "%s: Policy inconsistent with encryption context\n",
> +		       __func__);
> +		res = -EINVAL;
> +	}
> +out:
> +	return res;
> +}
> +
> +int ext4_is_child_context_consistent_with_parent(struct inode *parent,
> +						 struct inode *child)
> +{
> +	struct ext4_encryption_context parent_ctx, child_ctx;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &parent_ctx, sizeof(parent_ctx));
> +
> +	if (res != sizeof(parent_ctx))
> +		return 0;
> +	res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +			     &child_ctx, sizeof(child_ctx));
> +	if (res != sizeof(child_ctx))
> +		return 0;
> +	return (memcmp(parent_ctx.master_key_descriptor,
> +		       child_ctx.master_key_descriptor,
> +		       EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
> +		(parent_ctx.contents_encryption_mode ==
> +		 child_ctx.contents_encryption_mode) &&
> +		(parent_ctx.filenames_encryption_mode ==
> +		 child_ctx.filenames_encryption_mode));
> +}
> +
> +/**
> + * 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;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &ctx, sizeof(ctx));
> +
> +	if (res != sizeof(ctx))
> +		return -ENOENT;
> +
> +	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);
> +	return res;
> +}
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 2f3808e..fd2f3dd 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -588,6 +588,8 @@ enum {
> #define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
> #define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
> 
> +#include "ext4_crypto.h"
> +
> /*
>  * ioctl commands
>  */
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> new file mode 100644
> index 0000000..984ff38
> --- /dev/null
> +++ b/fs/ext4/ext4_crypto.h
> @@ -0,0 +1,54 @@
> +/*
> + * linux/fs/ext4/ext4_crypto.h
> + *
> + * 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 master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +} __attribute__((__packed__));

It wouldn't be bad to add a padding byte before master_key_descriptor,
even if this is only passed from the ioctl.  That allows casting the
key to a numeric value if desired without problems on some arches.

> +
> +#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0

Per comments in the call, it is better not to use "0" as a defined
version, since this is much more likely to be seen accidentally
(e.g. uninitialized buffer, memory corruption, etc).  Better to
start with version 1.

> +#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
> +
> +/**
> + * Encryption context for inode
> + *
> + * Protector format:
> + *  1 byte: Protector format (0 = this version)

Same here.

> + *  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 reserved;
> +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +	char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
> +} __attribute__((__packed__));
> +
> +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);
> +
> +#endif	/* _EXT4_CRYPTO_H */
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas





--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Theodore Ts'o April 11, 2015, 1:06 p.m. UTC | #3
On Mon, Apr 06, 2015 at 03:31:49PM -0600, Andreas Dilger wrote:
> > +/**
> > + * ext4_to_hex() - Converts to hexadecimal characters
> > + * @dst: Buffer to take hex character representation of contents of
> > + *       src. Must be at least of size (src_size * 2).
> > + * @src: Buffer to be converted to a hex string respresentation.
> > + * @src_size: Number of bytes to convert.
> > + */
> > +void ext4_to_hex(char *dst, char *src, size_t src_size)
> > +{
> > +	int x;
> > +
> > +	for (x = 0; x < src_size; x++)
> > +		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
> > +}
> 
> I think there is already some code in printk() to handle this?  Looking
> at vsnprintf->hex_string() it looks like "%*ph" would print out up to 64
> bytes as hex.

As it turns out we're not even using ext4_to_hex() any more, so we can
even more simply just delete this whole function.  :-)

							- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Theodore Ts'o April 11, 2015, 1:10 p.m. UTC | #4
On Wed, Apr 08, 2015 at 12:07:16PM -0600, Andreas Dilger wrote:
> > +/* Policy provided via an ioctl on the topmost directory */
> > +struct ext4_encryption_policy {
> > +	char version;
> > +	char contents_encryption_mode;
> > +	char filenames_encryption_mode;
> > +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> > +} __attribute__((__packed__));
> 
> It wouldn't be bad to add a padding byte before master_key_descriptor,
> even if this is only passed from the ioctl.  That allows casting the
> key to a numeric value if desired without problems on some arches.

This data structure is only used for the ioctl interface.  The on-disk
format is the struct ext4_encryption_context, where we do have the
padding.

> > +#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0
> 
> Per comments in the call, it is better not to use "0" as a defined
> version, since this is much more likely to be seen accidentally
> (e.g. uninitialized buffer, memory corruption, etc).  Better to
> start with version 1.

I'll make this change.

						- Ted

--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Theodore Ts'o April 11, 2015, 1:18 p.m. UTC | #5
On Sat, Apr 11, 2015 at 09:06:37AM -0400, Theodore Ts'o wrote:
> As it turns out we're not even using ext4_to_hex() any more, so we can
> even more simply just delete this whole function.  :-)

Whoops, spoke to soon; I'll just add the sprintf call directly into
fs/ext4/crypto_key.c.

						- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index cd6f50f..3886ee4 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,3 +12,4 @@  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
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
new file mode 100644
index 0000000..5cb4e74
--- /dev/null
+++ b/fs/ext4/crypto_policy.c
@@ -0,0 +1,155 @@ 
+/*
+ * linux/fs/ext4/crypto_policy.c
+ *
+ * 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.h"
+#include "xattr.h"
+
+/**
+ * ext4_to_hex() - Converts to hexadecimal characters
+ * @dst: Buffer to take hex character representation of contents of
+ *       src. Must be at least of size (src_size * 2).
+ * @src: Buffer to be converted to a hex string respresentation.
+ * @src_size: Number of bytes to convert.
+ */
+void ext4_to_hex(char *dst, char *src, size_t src_size)
+{
+	int x;
+
+	for (x = 0; x < src_size; x++)
+		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
+}
+
+/**
+ *
+ */
+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);
+}
+
+/**
+ * ext4_is_encryption_context_consistent_with_policy() - Checks whether the policy is consistent with the encryption context for the inode
+ * @inode:  ...
+ * @policy: ...
+ *
+ * Return ...
+ */
+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.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;
+	int res = 0;
+
+	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
+	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
+	       EXT4_KEY_DESCRIPTOR_SIZE);
+	ctx.contents_encryption_mode = policy->contents_encryption_mode;
+	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
+	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
+	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
+
+	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);
+	return res;
+}
+
+int ext4_process_policy(const struct ext4_encryption_policy *policy,
+			struct inode *inode)
+{
+	int res = 0;
+
+	if (!ext4_inode_has_encryption_context(inode)) {
+		res = ext4_create_encryption_context_from_policy(inode, policy);
+		goto out;
+	}
+
+	if (!ext4_is_encryption_context_consistent_with_policy(inode, policy)) {
+		printk(KERN_WARNING
+		       "%s: Policy inconsistent with encryption context\n",
+		       __func__);
+		res = -EINVAL;
+	}
+out:
+	return res;
+}
+
+int ext4_is_child_context_consistent_with_parent(struct inode *parent,
+						 struct inode *child)
+{
+	struct ext4_encryption_context parent_ctx, child_ctx;
+	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+				 &parent_ctx, sizeof(parent_ctx));
+
+	if (res != sizeof(parent_ctx))
+		return 0;
+	res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+			     &child_ctx, sizeof(child_ctx));
+	if (res != sizeof(child_ctx))
+		return 0;
+	return (memcmp(parent_ctx.master_key_descriptor,
+		       child_ctx.master_key_descriptor,
+		       EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+		(parent_ctx.contents_encryption_mode ==
+		 child_ctx.contents_encryption_mode) &&
+		(parent_ctx.filenames_encryption_mode ==
+		 child_ctx.filenames_encryption_mode));
+}
+
+/**
+ * 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;
+	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+				 &ctx, sizeof(ctx));
+
+	if (res != sizeof(ctx))
+		return -ENOENT;
+
+	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);
+	return res;
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 2f3808e..fd2f3dd 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -588,6 +588,8 @@  enum {
 #define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
 #define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
 
+#include "ext4_crypto.h"
+
 /*
  * ioctl commands
  */
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
new file mode 100644
index 0000000..984ff38
--- /dev/null
+++ b/fs/ext4/ext4_crypto.h
@@ -0,0 +1,54 @@ 
+/*
+ * linux/fs/ext4/ext4_crypto.h
+ *
+ * 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 master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0
+#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
+
+/**
+ * Encryption context for inode
+ *
+ * Protector format:
+ *  1 byte: Protector format (0 = 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 reserved;
+	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+	char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
+} __attribute__((__packed__));
+
+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);
+
+#endif	/* _EXT4_CRYPTO_H */