Re: [RFC] eCryptfs: Add mount option for cipher mode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2013-06-03 13:13:45, Will Morrison wrote:
> This patch removes the hardcoding of CBC mode in eCryptfs and adds a 
> mount option allowing one to specify the desired mode of operation. 
> Currently this defaults to cbc, but should would with any plain block 
> cipher mode supported by the kernel. We've been testing with ecb to 
> ensure we've found any implicit assumptions about cbc.
> 
> Future work will involve supporting AEAD modes (GCM) and adding in 
> integrity checking code.

Glad to hear that you all are making some headway.

> 
> As this is our first kernel patch, we'd like feedback on what we've 
> done right and what we've done wrong, not only in code, but also 
> anything related to the patch submission itself.

Good to know. I'll be a little picky and point out everything that I
see.

To start things off... when you have a side note, such as your paragraph
above mentioning that this is your first kernel patch, you don't want
something like that to end up in the commit log history forever. A good
place for notes like that is below the --- line and above the diff
command. Most patch merging tools should ignore that content and keep it
from being recorded forever in the commit history. (I know that you
didn't intend for this patch to be upstreamed at this time, but I
thought I'd mention this tip.)

> 
> Signed-off-by: William Morrison <camocrazed@xxxxxxxxx>
> ---
> diff -uprN -X ecryptfs_base/Documentation/dontdiff ecryptfs_base/fs/ecryptfs/crypto.c ecryptfs/fs/ecryptfs/crypto.c

I'll assume that this means that you're not using git. If you plan to
continue doing kernel development (or really any open source
development) after this feature, I'd recommend that you work on
acquiring basic git skills. You previously asked what can help speed up
your development workflow and understanding git is certainly something
that will help.

In fact, this patch won't apply for me using `git am -is /path/to/mbox`.
I'm not immediately sure why, but I do see some extra '-' characters
in the areas that git complains about.

It might be because you GPG-signed the email. GPG signing patches would
seem like a good thing to do, but Documentation/email-clients.txt claims
that it causes some problems and recommends that you don't sign patches.

Running the following search and replace in vim:

:%s/^- -/-/g

and stripping off the GPG header/footer, I'm kind of able to get the
patch to apply. There's offset and fuzz issues, so git am won't apply
it (but patch will, of course). Which kernel tree did you develop the
patch against?

> --- ecryptfs_base/fs/ecryptfs/crypto.c	2013-06-03 07:49:51.734430193 -0400
> +++ ecryptfs/fs/ecryptfs/crypto.c	2013-05-23 17:51:37.814355879 -0400
> @@ -743,7 +743,8 @@ int ecryptfs_init_crypt_ctx(struct ecryp
>  	}
>  	mutex_lock(&crypt_stat->cs_tfm_mutex);
>  	rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name,
> -						    crypt_stat->cipher, "cbc");
> +						crypt_stat->cipher,
> +						crypt_stat->cipher_mode);
>  	if (rc)
>  		goto out_unlock;
>  	crypt_stat->tfm = crypto_alloc_blkcipher(full_alg_name, 0,
> @@ -753,8 +754,9 @@ int ecryptfs_init_crypt_ctx(struct ecryp
>  		rc = PTR_ERR(crypt_stat->tfm);
>  		crypt_stat->tfm = NULL;
>  		ecryptfs_printk(KERN_ERR, "cryptfs: init_crypt_ctx(): "
> -				"Error initializing cipher [%s]\n",
> -				crypt_stat->cipher);
> +			"Error initializing cipher [%s] and mode [%s]\n",
> +			crypt_stat->cipher,
> +			crypt_stat->cipher_mode);
>  		goto out_unlock;
>  	}
>  	crypto_blkcipher_set_flags(crypt_stat->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
> @@ -916,6 +918,7 @@ static void ecryptfs_set_default_crypt_s
>  						      mount_crypt_stat);
>  	ecryptfs_set_default_sizes(crypt_stat);
>  	strcpy(crypt_stat->cipher, ECRYPTFS_DEFAULT_CIPHER);
> +	strcpy(crypt_stat->cipher_mode, ECRYPTFS_DEFAULT_CIPHER_MODE);
>  	crypt_stat->key_size = ECRYPTFS_DEFAULT_KEY_BYTES;
>  	crypt_stat->flags &= ~(ECRYPTFS_KEY_VALID);
>  	crypt_stat->file_version = ECRYPTFS_FILE_VERSION;
> @@ -949,6 +952,7 @@ int ecryptfs_new_file_context(struct ino
>  	    &ecryptfs_superblock_to_private(
>  		    ecryptfs_inode->i_sb)->mount_crypt_stat;
>  	int cipher_name_len;
> +	int cipher_mode_name_len;
>  	int rc = 0;
>  
>  	ecryptfs_set_default_crypt_stat_vals(crypt_stat, mount_crypt_stat);
> @@ -968,6 +972,12 @@ int ecryptfs_new_file_context(struct ino
>  	       mount_crypt_stat->global_default_cipher_name,
>  	       cipher_name_len);
>  	crypt_stat->cipher[cipher_name_len] = '\0';
> +	cipher_mode_name_len =
> +		strlen(mount_crypt_stat->global_default_cipher_mode_name);
> +	memcpy(crypt_stat->cipher_mode,
> +		mount_crypt_stat->global_default_cipher_mode_name,
> +		cipher_mode_name_len);
> +	crypt_stat->cipher_mode[cipher_mode_name_len] = '\0';
>  	crypt_stat->key_size =
>  		mount_crypt_stat->global_default_cipher_key_size;
>  	ecryptfs_generate_new_key(crypt_stat);
> diff -uprN -X ecryptfs_base/Documentation/dontdiff ecryptfs_base/fs/ecryptfs/ecryptfs_kernel.h ecryptfs/fs/ecryptfs/ecryptfs_kernel.h
> --- ecryptfs_base/fs/ecryptfs/ecryptfs_kernel.h	2013-06-03 07:49:51.734430193 -0400
> +++ ecryptfs/fs/ecryptfs/ecryptfs_kernel.h	2013-05-23 17:51:37.814355879 -0400
> @@ -124,6 +124,7 @@ ecryptfs_get_key_payload_data(struct key
>  
>  #define ECRYPTFS_MAX_KEYSET_SIZE 1024
>  #define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32
> +#define ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE 32
>  #define ECRYPTFS_MAX_NUM_ENC_KEYS 64
>  #define ECRYPTFS_MAX_IV_BYTES 16	/* 128 bits */
>  #define ECRYPTFS_SALT_BYTES 2
> @@ -133,6 +134,7 @@ ecryptfs_get_key_payload_data(struct key
>  #define ECRYPTFS_SIZE_AND_MARKER_BYTES (ECRYPTFS_FILE_SIZE_BYTES \
>  					+ MAGIC_ECRYPTFS_MARKER_SIZE_BYTES)
>  #define ECRYPTFS_DEFAULT_CIPHER "aes"
> +#define ECRYPTFS_DEFAULT_CIPHER_MODE "cbc"
>  #define ECRYPTFS_DEFAULT_KEY_BYTES 16
>  #define ECRYPTFS_DEFAULT_HASH "md5"
>  #define ECRYPTFS_TAG_70_DIGEST ECRYPTFS_DEFAULT_HASH
> @@ -237,6 +239,7 @@ struct ecryptfs_crypt_stat {
>  	struct crypto_hash *hash_tfm; /* Crypto context for generating
>  				       * the initialization vectors */
>  	unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
> +	unsigned char cipher_mode[ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE];
>  	unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
>  	unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES];
>  	struct list_head keysig_list;
> @@ -338,6 +341,8 @@ struct ecryptfs_mount_crypt_stat {
>  	size_t global_default_fn_cipher_key_bytes;
>  	unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE
>  						 + 1];
> +	unsigned char global_default_cipher_mode_name[
> +		ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE + 1];
>  	unsigned char global_default_fn_cipher_name[
>  		ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
>  	char global_default_fnek_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
> diff -uprN -X ecryptfs_base/Documentation/dontdiff ecryptfs_base/fs/ecryptfs/keystore.c ecryptfs/fs/ecryptfs/keystore.c
> --- ecryptfs_base/fs/ecryptfs/keystore.c	2013-06-03 07:49:51.734430193 -0400
> +++ ecryptfs/fs/ecryptfs/keystore.c	2013-06-03 10:45:56.142519966 -0400
> @@ -1467,6 +1467,9 @@ parse_tag_3_packet(struct ecryptfs_crypt
>  		crypt_stat->key_size =
>  			(*new_auth_tok)->session_key.encrypted_key_size;
>  	}
> +	strncpy(crypt_stat->cipher_mode,
> +		crypt_stat->mount_crypt_stat->global_default_cipher_mode_name,
> +		ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE);

This is a problem. eCryptfs files are atomic in that they contain all of
the information needed to decrypt the file (except for the FEKEK) in the
header. So a single eCryptfs mount may contain files encrypted by
AES-128, AES-256, blowfish, the FEK can be wrapped with a public key,
multiple FEKEKs can be used, etc., and eCryptfs can seamlessly handle
it because of the various metadata stored in the file header.

At first glance, it doesn't look like RFC2440 defines a field for
different cipher modes. eCryptfs has always just made the assumption
that it should use its tweaked CBC mode and that has always been
sufficient.

We will need a place to store the cipher mode in the header and that
field will need to be read in somewhere around here.

Also, when writing out the header information, we'll need to store the
cipher mode.

>  	rc = ecryptfs_init_crypt_ctx(crypt_stat);
>  	if (rc)
>  		goto out_free;
> diff -uprN -X ecryptfs_base/Documentation/dontdiff ecryptfs_base/fs/ecryptfs/main.c ecryptfs/fs/ecryptfs/main.c
> --- ecryptfs_base/fs/ecryptfs/main.c	2013-06-03 07:49:51.734430193 -0400
> +++ ecryptfs/fs/ecryptfs/main.c	2013-06-03 10:54:26.570524304 -0400
> @@ -171,6 +171,7 @@ void ecryptfs_put_lower_file(struct inod
>  
>  enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig,
>         ecryptfs_opt_cipher, ecryptfs_opt_ecryptfs_cipher,
> +       ecryptfs_opt_cipher_mode, ecryptfs_opt_ecryptfs_cipher_mode,
>         ecryptfs_opt_ecryptfs_key_bytes,
>         ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata,
>         ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig,
> @@ -183,7 +184,9 @@ static const match_table_t tokens = {
>  	{ecryptfs_opt_sig, "sig=%s"},
>  	{ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"},
>  	{ecryptfs_opt_cipher, "cipher=%s"},
> +	{ecryptfs_opt_cipher_mode, "cipher_mode=%s"},
>  	{ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"},
> +	{ecryptfs_opt_ecryptfs_cipher_mode, "ecryptfs_cipher_mode=%s"},
>  	{ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"},
>  	{ecryptfs_opt_passthrough, "ecryptfs_passthrough"},
>  	{ecryptfs_opt_xattr_metadata, "ecryptfs_xattr_metadata"},
> @@ -258,10 +261,13 @@ static void ecryptfs_init_mount_crypt_st
>  static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
>  				  uid_t *check_ruid)
>  {
> +	const char *mode_white_list[] = {"cbc", "ecb", "gcm"};

When you submit a patch, it should be able to stand on its own. This
patch doesn't stand on its own because ecb and gcm aren't supported but
this patch happily accepts them. This white list should only contain
"cbc" at this point.

The idea you had with testing with ECB was a good idea, but it isn't
something that we'd want to ever support in eCryptfs since our default
is more secure. So the "ecb" element should of just been a local change
that you made on top of this patch, only for testing.

>  	char *p;
> +	int i = 0;
>  	int rc = 0;
>  	int sig_set = 0;
>  	int cipher_name_set = 0;
> +	int cipher_mode_name_set = 0;
>  	int fn_cipher_name_set = 0;
>  	int cipher_key_bytes;
>  	int cipher_key_bytes_set = 0;
> @@ -274,6 +280,8 @@ static int ecryptfs_parse_options(struct
>  	char *sig_src;
>  	char *cipher_name_dst;
>  	char *cipher_name_src;
> +	char *cipher_mode_name_dst;
> +	char *cipher_mode_name_src;
>  	char *fn_cipher_name_dst;
>  	char *fn_cipher_name_src;
>  	char *fnek_dst;
> @@ -317,6 +325,18 @@ static int ecryptfs_parse_options(struct
>  			cipher_name_dst[ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0';
>  			cipher_name_set = 1;
>  			break;
> +		case ecryptfs_opt_cipher_mode:
> +		case ecryptfs_opt_ecryptfs_cipher_mode:
> +			cipher_mode_name_src = args[0].from;
> +			cipher_mode_name_dst =
> +				mount_crypt_stat->
> +				global_default_cipher_mode_name;

Yuck. I don't know if this is better or worse than breaking the 80 char
rule. eCryptfs uses variable names that are too long in most cases.
(This isn't something that you need to worry about)

> +			strncpy(cipher_mode_name_dst, cipher_mode_name_src,
> +				ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE);
> +			cipher_mode_name_dst[
> +				ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE] = '\0';
> +			cipher_mode_name_set = 1;
> +			break;
>  		case ecryptfs_opt_ecryptfs_key_bytes:
>  			cipher_key_bytes_src = args[0].from;
>  			cipher_key_bytes =
> @@ -412,6 +432,14 @@ static int ecryptfs_parse_options(struct
>  		strcpy(mount_crypt_stat->global_default_cipher_name,
>  		       ECRYPTFS_DEFAULT_CIPHER);
>  	}
> +	if (!cipher_mode_name_set) {
> +		int cipher_mode_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER_MODE);
> +
> +		BUG_ON(cipher_mode_name_len >=
> +			ECRYPTFS_MAX_CIPHER_MODE_NAME_SIZE);

I see that you copied and pasted this logic from the !cipher_name_set
conditional. So it isn't your fault, but this is terrible. :)

This is something that should be caught at build time rather than at
runtime (why force this BUG() on our users?). These BUG_ON()'s should
probably be replaced with a BUILD_BUG_ON().

> +		strcpy(mount_crypt_stat->global_default_cipher_mode_name,
> +		       ECRYPTFS_DEFAULT_CIPHER_MODE);
> +	}
>  	if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)
>  	    && !fn_cipher_name_set)
>  		strcpy(mount_crypt_stat->global_default_fn_cipher_name,
> @@ -434,6 +462,22 @@ static int ecryptfs_parse_options(struct
>  		goto out;
>  	}
>  
> +	rc = -EINVAL;
> +	for (i = 0; i < ARRAY_SIZE(mode_white_list); i++) {
> +		if (strcmp(mount_crypt_stat->global_default_cipher_mode_name,
> +				mode_white_list[i]) == 0) {
> +			rc = 0;
> +			break;
> +		}
> +	}
> +	if (rc) {
> +		ecryptfs_printk(KERN_ERR,
> +				"eCryptfs doesn't support cipher mode: %s",
> +				mount_crypt_stat->
> +					global_default_cipher_mode_name);
> +		goto out;
> +	}
> +
>  	mutex_lock(&key_tfm_list_mutex);
>  	if (!ecryptfs_tfm_exists(mount_crypt_stat->global_default_cipher_name,
>  				 NULL)) {
> --

This was a nice submission for your first shot at it. The biggest
problem to fix will be figuring out where to store the mode in the
header. Take a look through RFC2440 (and 4880 and 5581) to see if
there's already a field we can use for this. If not, we'll have to give
it some more thought. I don't want to rely on the user specifying the
correct ecryptfs_cipher_mode mount option.

Thanks again for working on this!

Tyler

Attachment: signature.asc
Description: Digital signature


[Index of Archives]     [Linux Crypto]     [Device Mapper Crypto]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux