|
@@ -402,6 +402,569 @@ out:
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+ecryptfs_find_global_auth_tok_for_sig(
|
|
|
|
+ struct ecryptfs_global_auth_tok **global_auth_tok,
|
|
|
|
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig)
|
|
|
|
+{
|
|
|
|
+ struct ecryptfs_global_auth_tok *walker;
|
|
|
|
+ int rc = 0;
|
|
|
|
+
|
|
|
|
+ (*global_auth_tok) = NULL;
|
|
|
|
+ mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
|
|
|
|
+ list_for_each_entry(walker,
|
|
|
|
+ &mount_crypt_stat->global_auth_tok_list,
|
|
|
|
+ mount_crypt_stat_list) {
|
|
|
|
+ if (memcmp(walker->sig, sig, ECRYPTFS_SIG_SIZE_HEX) == 0) {
|
|
|
|
+ (*global_auth_tok) = walker;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+out:
|
|
|
|
+ mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * ecryptfs_find_auth_tok_for_sig
|
|
|
|
+ * @auth_tok: Set to the matching auth_tok; NULL if not found
|
|
|
|
+ * @crypt_stat: inode crypt_stat crypto context
|
|
|
|
+ * @sig: Sig of auth_tok to find
|
|
|
|
+ *
|
|
|
|
+ * For now, this function simply looks at the registered auth_tok's
|
|
|
|
+ * linked off the mount_crypt_stat, so all the auth_toks that can be
|
|
|
|
+ * used must be registered at mount time. This function could
|
|
|
|
+ * potentially try a lot harder to find auth_tok's (e.g., by calling
|
|
|
|
+ * out to ecryptfsd to dynamically retrieve an auth_tok object) so
|
|
|
|
+ * that static registration of auth_tok's will no longer be necessary.
|
|
|
|
+ *
|
|
|
|
+ * Returns zero on no error; non-zero on error
|
|
|
|
+ */
|
|
|
|
+static int
|
|
|
|
+ecryptfs_find_auth_tok_for_sig(
|
|
|
|
+ struct ecryptfs_auth_tok **auth_tok,
|
|
|
|
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
|
|
|
|
+ char *sig)
|
|
|
|
+{
|
|
|
|
+ struct ecryptfs_global_auth_tok *global_auth_tok;
|
|
|
|
+ int rc = 0;
|
|
|
|
+
|
|
|
|
+ (*auth_tok) = NULL;
|
|
|
|
+ if (ecryptfs_find_global_auth_tok_for_sig(&global_auth_tok,
|
|
|
|
+ mount_crypt_stat, sig)) {
|
|
|
|
+ struct key *auth_tok_key;
|
|
|
|
+
|
|
|
|
+ rc = ecryptfs_keyring_auth_tok_for_sig(&auth_tok_key, auth_tok,
|
|
|
|
+ sig);
|
|
|
|
+ } else
|
|
|
|
+ (*auth_tok) = global_auth_tok->global_auth_tok;
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * write_tag_70_packet can gobble a lot of stack space. We stuff most
|
|
|
|
+ * of the function's parameters in a kmalloc'd struct to help reduce
|
|
|
|
+ * eCryptfs' overall stack usage.
|
|
|
|
+ */
|
|
|
|
+struct ecryptfs_write_tag_70_packet_silly_stack {
|
|
|
|
+ u8 cipher_code;
|
|
|
|
+ size_t max_packet_size;
|
|
|
|
+ size_t packet_size_len;
|
|
|
|
+ size_t block_aligned_filename_size;
|
|
|
|
+ size_t block_size;
|
|
|
|
+ size_t i;
|
|
|
|
+ size_t j;
|
|
|
|
+ size_t num_rand_bytes;
|
|
|
|
+ struct mutex *tfm_mutex;
|
|
|
|
+ char *block_aligned_filename;
|
|
|
|
+ struct ecryptfs_auth_tok *auth_tok;
|
|
|
|
+ struct scatterlist src_sg;
|
|
|
|
+ struct scatterlist dst_sg;
|
|
|
|
+ struct blkcipher_desc desc;
|
|
|
|
+ char iv[ECRYPTFS_MAX_IV_BYTES];
|
|
|
|
+ char hash[ECRYPTFS_TAG_70_DIGEST_SIZE];
|
|
|
|
+ char tmp_hash[ECRYPTFS_TAG_70_DIGEST_SIZE];
|
|
|
|
+ struct hash_desc hash_desc;
|
|
|
|
+ struct scatterlist hash_sg;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * write_tag_70_packet - Write encrypted filename (EFN) packet against FNEK
|
|
|
|
+ * @filename: NULL-terminated filename string
|
|
|
|
+ *
|
|
|
|
+ * This is the simplest mechanism for achieving filename encryption in
|
|
|
|
+ * eCryptfs. It encrypts the given filename with the mount-wide
|
|
|
|
+ * filename encryption key (FNEK) and stores it in a packet to @dest,
|
|
|
|
+ * which the callee will encode and write directly into the dentry
|
|
|
|
+ * name.
|
|
|
|
+ */
|
|
|
|
+int
|
|
|
|
+ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
|
|
|
|
+ size_t *packet_size,
|
|
|
|
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
|
|
|
|
+ char *filename, size_t filename_size)
|
|
|
|
+{
|
|
|
|
+ struct ecryptfs_write_tag_70_packet_silly_stack *s;
|
|
|
|
+ int rc = 0;
|
|
|
|
+
|
|
|
|
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
|
|
+ if (!s) {
|
|
|
|
+ printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
|
|
|
|
+ "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
|
|
+ (*packet_size) = 0;
|
|
|
|
+ rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(
|
|
|
|
+ &s->desc.tfm,
|
|
|
|
+ &s->tfm_mutex, mount_crypt_stat->global_default_fn_cipher_name);
|
|
|
|
+ if (unlikely(rc)) {
|
|
|
|
+ printk(KERN_ERR "Internal error whilst attempting to get "
|
|
|
|
+ "tfm and mutex for cipher name [%s]; rc = [%d]\n",
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_name, rc);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ mutex_lock(s->tfm_mutex);
|
|
|
|
+ s->block_size = crypto_blkcipher_blocksize(s->desc.tfm);
|
|
|
|
+ /* Plus one for the \0 separator between the random prefix
|
|
|
|
+ * and the plaintext filename */
|
|
|
|
+ s->num_rand_bytes = (ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES + 1);
|
|
|
|
+ s->block_aligned_filename_size = (s->num_rand_bytes + filename_size);
|
|
|
|
+ if ((s->block_aligned_filename_size % s->block_size) != 0) {
|
|
|
|
+ s->num_rand_bytes += (s->block_size
|
|
|
|
+ - (s->block_aligned_filename_size
|
|
|
|
+ % s->block_size));
|
|
|
|
+ s->block_aligned_filename_size = (s->num_rand_bytes
|
|
|
|
+ + filename_size);
|
|
|
|
+ }
|
|
|
|
+ /* Octet 0: Tag 70 identifier
|
|
|
|
+ * Octets 1-N1: Tag 70 packet size (includes cipher identifier
|
|
|
|
+ * and block-aligned encrypted filename size)
|
|
|
|
+ * Octets N1-N2: FNEK sig (ECRYPTFS_SIG_SIZE)
|
|
|
|
+ * Octet N2-N3: Cipher identifier (1 octet)
|
|
|
|
+ * Octets N3-N4: Block-aligned encrypted filename
|
|
|
|
+ * - Consists of a minimum number of random characters, a \0
|
|
|
|
+ * separator, and then the filename */
|
|
|
|
+ s->max_packet_size = (1 /* Tag 70 identifier */
|
|
|
|
+ + 3 /* Max Tag 70 packet size */
|
|
|
|
+ + ECRYPTFS_SIG_SIZE /* FNEK sig */
|
|
|
|
+ + 1 /* Cipher identifier */
|
|
|
|
+ + s->block_aligned_filename_size);
|
|
|
|
+ if (dest == NULL) {
|
|
|
|
+ (*packet_size) = s->max_packet_size;
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+ if (s->max_packet_size > (*remaining_bytes)) {
|
|
|
|
+ printk(KERN_WARNING "%s: Require [%d] bytes to write; only "
|
|
|
|
+ "[%d] available\n", __func__, s->max_packet_size,
|
|
|
|
+ (*remaining_bytes));
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+ s->block_aligned_filename = kzalloc(s->block_aligned_filename_size,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (!s->block_aligned_filename) {
|
|
|
|
+ printk(KERN_ERR "%s: Out of kernel memory whilst attempting to "
|
|
|
|
+ "kzalloc [%Zd] bytes\n", __func__,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ rc = -ENOMEM;
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+ s->i = 0;
|
|
|
|
+ dest[s->i++] = ECRYPTFS_TAG_70_PACKET_TYPE;
|
|
|
|
+ rc = ecryptfs_write_packet_length(&dest[s->i],
|
|
|
|
+ (ECRYPTFS_SIG_SIZE
|
|
|
|
+ + 1 /* Cipher code */
|
|
|
|
+ + s->block_aligned_filename_size),
|
|
|
|
+ &s->packet_size_len);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR "%s: Error generating tag 70 packet "
|
|
|
|
+ "header; cannot generate packet length; rc = [%d]\n",
|
|
|
|
+ __func__, rc);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ s->i += s->packet_size_len;
|
|
|
|
+ ecryptfs_from_hex(&dest[s->i],
|
|
|
|
+ mount_crypt_stat->global_default_fnek_sig,
|
|
|
|
+ ECRYPTFS_SIG_SIZE);
|
|
|
|
+ s->i += ECRYPTFS_SIG_SIZE;
|
|
|
|
+ s->cipher_code = ecryptfs_code_for_cipher_string(
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_name,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
|
|
|
|
+ if (s->cipher_code == 0) {
|
|
|
|
+ printk(KERN_WARNING "%s: Unable to generate code for "
|
|
|
|
+ "cipher [%s] with key bytes [%d]\n", __func__,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_name,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ dest[s->i++] = s->cipher_code;
|
|
|
|
+ rc = ecryptfs_find_auth_tok_for_sig(
|
|
|
|
+ &s->auth_tok, mount_crypt_stat,
|
|
|
|
+ mount_crypt_stat->global_default_fnek_sig);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR "%s: Error attempting to find auth tok for "
|
|
|
|
+ "fnek sig [%s]; rc = [%d]\n", __func__,
|
|
|
|
+ mount_crypt_stat->global_default_fnek_sig, rc);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ /* TODO: Support other key modules than passphrase for
|
|
|
|
+ * filename encryption */
|
|
|
|
+ BUG_ON(s->auth_tok->token_type != ECRYPTFS_PASSWORD);
|
|
|
|
+ sg_init_one(
|
|
|
|
+ &s->hash_sg,
|
|
|
|
+ (u8 *)s->auth_tok->token.password.session_key_encryption_key,
|
|
|
|
+ s->auth_tok->token.password.session_key_encryption_key_bytes);
|
|
|
|
+ s->hash_desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
|
|
+ s->hash_desc.tfm = crypto_alloc_hash(ECRYPTFS_TAG_70_DIGEST, 0,
|
|
|
|
+ CRYPTO_ALG_ASYNC);
|
|
|
|
+ if (IS_ERR(s->hash_desc.tfm)) {
|
|
|
|
+ rc = PTR_ERR(s->hash_desc.tfm);
|
|
|
|
+ printk(KERN_ERR "%s: Error attempting to "
|
|
|
|
+ "allocate hash crypto context; rc = [%d]\n",
|
|
|
|
+ __func__, rc);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_hash_init(&s->hash_desc);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Error initializing crypto hash; rc = [%d]\n",
|
|
|
|
+ __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_hash_update(
|
|
|
|
+ &s->hash_desc, &s->hash_sg,
|
|
|
|
+ s->auth_tok->token.password.session_key_encryption_key_bytes);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Error updating crypto hash; rc = [%d]\n",
|
|
|
|
+ __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_hash_final(&s->hash_desc, s->hash);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Error finalizing crypto hash; rc = [%d]\n",
|
|
|
|
+ __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ for (s->j = 0; s->j < (s->num_rand_bytes - 1); s->j++) {
|
|
|
|
+ s->block_aligned_filename[s->j] =
|
|
|
|
+ s->hash[(s->j % ECRYPTFS_TAG_70_DIGEST_SIZE)];
|
|
|
|
+ if ((s->j % ECRYPTFS_TAG_70_DIGEST_SIZE)
|
|
|
|
+ == (ECRYPTFS_TAG_70_DIGEST_SIZE - 1)) {
|
|
|
|
+ sg_init_one(&s->hash_sg, (u8 *)s->hash,
|
|
|
|
+ ECRYPTFS_TAG_70_DIGEST_SIZE);
|
|
|
|
+ rc = crypto_hash_init(&s->hash_desc);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Error initializing crypto hash; "
|
|
|
|
+ "rc = [%d]\n", __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_hash_update(&s->hash_desc, &s->hash_sg,
|
|
|
|
+ ECRYPTFS_TAG_70_DIGEST_SIZE);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Error updating crypto hash; "
|
|
|
|
+ "rc = [%d]\n", __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_hash_final(&s->hash_desc, s->tmp_hash);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Error finalizing crypto hash; "
|
|
|
|
+ "rc = [%d]\n", __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ memcpy(s->hash, s->tmp_hash,
|
|
|
|
+ ECRYPTFS_TAG_70_DIGEST_SIZE);
|
|
|
|
+ }
|
|
|
|
+ if (s->block_aligned_filename[s->j] == '\0')
|
|
|
|
+ s->block_aligned_filename[s->j] = ECRYPTFS_NON_NULL;
|
|
|
|
+ }
|
|
|
|
+ memcpy(&s->block_aligned_filename[s->num_rand_bytes], filename,
|
|
|
|
+ filename_size);
|
|
|
|
+ rc = virt_to_scatterlist(s->block_aligned_filename,
|
|
|
|
+ s->block_aligned_filename_size, &s->src_sg, 1);
|
|
|
|
+ if (rc != 1) {
|
|
|
|
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
|
|
|
|
+ "convert filename memory to scatterlist; "
|
|
|
|
+ "expected rc = 1; got rc = [%d]. "
|
|
|
|
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = virt_to_scatterlist(&dest[s->i], s->block_aligned_filename_size,
|
|
|
|
+ &s->dst_sg, 1);
|
|
|
|
+ if (rc != 1) {
|
|
|
|
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
|
|
|
|
+ "convert encrypted filename memory to scatterlist; "
|
|
|
|
+ "expected rc = 1; got rc = [%d]. "
|
|
|
|
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ /* The characters in the first block effectively do the job
|
|
|
|
+ * of the IV here, so we just use 0's for the IV. Note the
|
|
|
|
+ * constraint that ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES
|
|
|
|
+ * >= ECRYPTFS_MAX_IV_BYTES. */
|
|
|
|
+ memset(s->iv, 0, ECRYPTFS_MAX_IV_BYTES);
|
|
|
|
+ s->desc.info = s->iv;
|
|
|
|
+ rc = crypto_blkcipher_setkey(
|
|
|
|
+ s->desc.tfm,
|
|
|
|
+ s->auth_tok->token.password.session_key_encryption_key,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
|
|
|
|
+ if (rc < 0) {
|
|
|
|
+ printk(KERN_ERR "%s: Error setting key for crypto context; "
|
|
|
|
+ "rc = [%d]. s->auth_tok->token.password.session_key_"
|
|
|
|
+ "encryption_key = [0x%p]; mount_crypt_stat->"
|
|
|
|
+ "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
|
|
|
|
+ rc,
|
|
|
|
+ s->auth_tok->token.password.session_key_encryption_key,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_blkcipher_encrypt_iv(&s->desc, &s->dst_sg, &s->src_sg,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR "%s: Error attempting to encrypt filename; "
|
|
|
|
+ "rc = [%d]\n", __func__, rc);
|
|
|
|
+ goto out_release_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ s->i += s->block_aligned_filename_size;
|
|
|
|
+ (*packet_size) = s->i;
|
|
|
|
+ (*remaining_bytes) -= (*packet_size);
|
|
|
|
+out_release_free_unlock:
|
|
|
|
+ crypto_free_hash(s->hash_desc.tfm);
|
|
|
|
+out_free_unlock:
|
|
|
|
+ memset(s->block_aligned_filename, 0, s->block_aligned_filename_size);
|
|
|
|
+ kfree(s->block_aligned_filename);
|
|
|
|
+out_unlock:
|
|
|
|
+ mutex_unlock(s->tfm_mutex);
|
|
|
|
+out:
|
|
|
|
+ kfree(s);
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct ecryptfs_parse_tag_70_packet_silly_stack {
|
|
|
|
+ u8 cipher_code;
|
|
|
|
+ size_t max_packet_size;
|
|
|
|
+ size_t packet_size_len;
|
|
|
|
+ size_t parsed_tag_70_packet_size;
|
|
|
|
+ size_t block_aligned_filename_size;
|
|
|
|
+ size_t block_size;
|
|
|
|
+ size_t i;
|
|
|
|
+ struct mutex *tfm_mutex;
|
|
|
|
+ char *decrypted_filename;
|
|
|
|
+ struct ecryptfs_auth_tok *auth_tok;
|
|
|
|
+ struct scatterlist src_sg;
|
|
|
|
+ struct scatterlist dst_sg;
|
|
|
|
+ struct blkcipher_desc desc;
|
|
|
|
+ char fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX + 1];
|
|
|
|
+ char iv[ECRYPTFS_MAX_IV_BYTES];
|
|
|
|
+ char cipher_string[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * parse_tag_70_packet - Parse and process FNEK-encrypted passphrase packet
|
|
|
|
+ * @filename: This function kmalloc's the memory for the filename
|
|
|
|
+ */
|
|
|
|
+int
|
|
|
|
+ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
|
|
|
|
+ size_t *packet_size,
|
|
|
|
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
|
|
|
|
+ char *data, size_t max_packet_size)
|
|
|
|
+{
|
|
|
|
+ struct ecryptfs_parse_tag_70_packet_silly_stack *s;
|
|
|
|
+ int rc = 0;
|
|
|
|
+
|
|
|
|
+ (*packet_size) = 0;
|
|
|
|
+ (*filename_size) = 0;
|
|
|
|
+ (*filename) = NULL;
|
|
|
|
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
|
|
+ if (!s) {
|
|
|
|
+ printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
|
|
|
|
+ "[%d] bytes of kernel memory\n", __func__, sizeof(*s));
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
|
|
+ if (max_packet_size < (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)) {
|
|
|
|
+ printk(KERN_WARNING "%s: max_packet_size is [%Zd]; it must be "
|
|
|
|
+ "at least [%d]\n", __func__, max_packet_size,
|
|
|
|
+ (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1));
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ /* Octet 0: Tag 70 identifier
|
|
|
|
+ * Octets 1-N1: Tag 70 packet size (includes cipher identifier
|
|
|
|
+ * and block-aligned encrypted filename size)
|
|
|
|
+ * Octets N1-N2: FNEK sig (ECRYPTFS_SIG_SIZE)
|
|
|
|
+ * Octet N2-N3: Cipher identifier (1 octet)
|
|
|
|
+ * Octets N3-N4: Block-aligned encrypted filename
|
|
|
|
+ * - Consists of a minimum number of random numbers, a \0
|
|
|
|
+ * separator, and then the filename */
|
|
|
|
+ if (data[(*packet_size)++] != ECRYPTFS_TAG_70_PACKET_TYPE) {
|
|
|
|
+ printk(KERN_WARNING "%s: Invalid packet tag [0x%.2x]; must be "
|
|
|
|
+ "tag [0x%.2x]\n", __func__,
|
|
|
|
+ data[((*packet_size) - 1)], ECRYPTFS_TAG_70_PACKET_TYPE);
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ rc = ecryptfs_parse_packet_length(&data[(*packet_size)],
|
|
|
|
+ &s->parsed_tag_70_packet_size,
|
|
|
|
+ &s->packet_size_len);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_WARNING "%s: Error parsing packet length; "
|
|
|
|
+ "rc = [%d]\n", __func__, rc);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ s->block_aligned_filename_size = (s->parsed_tag_70_packet_size
|
|
|
|
+ - ECRYPTFS_SIG_SIZE - 1);
|
|
|
|
+ if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
|
|
|
|
+ > max_packet_size) {
|
|
|
|
+ printk(KERN_WARNING "%s: max_packet_size is [%d]; real packet "
|
|
|
|
+ "size is [%d]\n", __func__, max_packet_size,
|
|
|
|
+ (1 + s->packet_size_len + 1
|
|
|
|
+ + s->block_aligned_filename_size));
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ (*packet_size) += s->packet_size_len;
|
|
|
|
+ ecryptfs_to_hex(s->fnek_sig_hex, &data[(*packet_size)],
|
|
|
|
+ ECRYPTFS_SIG_SIZE);
|
|
|
|
+ s->fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX] = '\0';
|
|
|
|
+ (*packet_size) += ECRYPTFS_SIG_SIZE;
|
|
|
|
+ s->cipher_code = data[(*packet_size)++];
|
|
|
|
+ rc = ecryptfs_cipher_code_to_string(s->cipher_string, s->cipher_code);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_WARNING "%s: Cipher code [%d] is invalid\n",
|
|
|
|
+ __func__, s->cipher_code);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(&s->desc.tfm,
|
|
|
|
+ &s->tfm_mutex,
|
|
|
|
+ s->cipher_string);
|
|
|
|
+ if (unlikely(rc)) {
|
|
|
|
+ printk(KERN_ERR "Internal error whilst attempting to get "
|
|
|
|
+ "tfm and mutex for cipher name [%s]; rc = [%d]\n",
|
|
|
|
+ s->cipher_string, rc);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ mutex_lock(s->tfm_mutex);
|
|
|
|
+ rc = virt_to_scatterlist(&data[(*packet_size)],
|
|
|
|
+ s->block_aligned_filename_size, &s->src_sg, 1);
|
|
|
|
+ if (rc != 1) {
|
|
|
|
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
|
|
|
|
+ "convert encrypted filename memory to scatterlist; "
|
|
|
|
+ "expected rc = 1; got rc = [%d]. "
|
|
|
|
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+ (*packet_size) += s->block_aligned_filename_size;
|
|
|
|
+ s->decrypted_filename = kmalloc(s->block_aligned_filename_size,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (!s->decrypted_filename) {
|
|
|
|
+ printk(KERN_ERR "%s: Out of memory whilst attempting to "
|
|
|
|
+ "kmalloc [%d] bytes\n", __func__,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ rc = -ENOMEM;
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = virt_to_scatterlist(s->decrypted_filename,
|
|
|
|
+ s->block_aligned_filename_size, &s->dst_sg, 1);
|
|
|
|
+ if (rc != 1) {
|
|
|
|
+ printk(KERN_ERR "%s: Internal error whilst attempting to "
|
|
|
|
+ "convert decrypted filename memory to scatterlist; "
|
|
|
|
+ "expected rc = 1; got rc = [%d]. "
|
|
|
|
+ "block_aligned_filename_size = [%d]\n", __func__, rc,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ /* The characters in the first block effectively do the job of
|
|
|
|
+ * the IV here, so we just use 0's for the IV. Note the
|
|
|
|
+ * constraint that ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES
|
|
|
|
+ * >= ECRYPTFS_MAX_IV_BYTES. */
|
|
|
|
+ memset(s->iv, 0, ECRYPTFS_MAX_IV_BYTES);
|
|
|
|
+ s->desc.info = s->iv;
|
|
|
|
+ rc = ecryptfs_find_auth_tok_for_sig(&s->auth_tok, mount_crypt_stat,
|
|
|
|
+ s->fnek_sig_hex);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR "%s: Error attempting to find auth tok for "
|
|
|
|
+ "fnek sig [%s]; rc = [%d]\n", __func__, s->fnek_sig_hex,
|
|
|
|
+ rc);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ /* TODO: Support other key modules than passphrase for
|
|
|
|
+ * filename encryption */
|
|
|
|
+ BUG_ON(s->auth_tok->token_type != ECRYPTFS_PASSWORD);
|
|
|
|
+ rc = crypto_blkcipher_setkey(
|
|
|
|
+ s->desc.tfm,
|
|
|
|
+ s->auth_tok->token.password.session_key_encryption_key,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
|
|
|
|
+ if (rc < 0) {
|
|
|
|
+ printk(KERN_ERR "%s: Error setting key for crypto context; "
|
|
|
|
+ "rc = [%d]. s->auth_tok->token.password.session_key_"
|
|
|
|
+ "encryption_key = [0x%p]; mount_crypt_stat->"
|
|
|
|
+ "global_default_fn_cipher_key_bytes = [%Zd]\n", __func__,
|
|
|
|
+ rc,
|
|
|
|
+ s->auth_tok->token.password.session_key_encryption_key,
|
|
|
|
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ rc = crypto_blkcipher_decrypt_iv(&s->desc, &s->dst_sg, &s->src_sg,
|
|
|
|
+ s->block_aligned_filename_size);
|
|
|
|
+ if (rc) {
|
|
|
|
+ printk(KERN_ERR "%s: Error attempting to decrypt filename; "
|
|
|
|
+ "rc = [%d]\n", __func__, rc);
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ s->i = 0;
|
|
|
|
+ while (s->decrypted_filename[s->i] != '\0'
|
|
|
|
+ && s->i < s->block_aligned_filename_size)
|
|
|
|
+ s->i++;
|
|
|
|
+ if (s->i == s->block_aligned_filename_size) {
|
|
|
|
+ printk(KERN_WARNING "%s: Invalid tag 70 packet; could not "
|
|
|
|
+ "find valid separator between random characters and "
|
|
|
|
+ "the filename\n", __func__);
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ s->i++;
|
|
|
|
+ (*filename_size) = (s->block_aligned_filename_size - s->i);
|
|
|
|
+ if (!((*filename_size) > 0 && (*filename_size < PATH_MAX))) {
|
|
|
|
+ printk(KERN_WARNING "%s: Filename size is [%Zd], which is "
|
|
|
|
+ "invalid\n", __func__, (*filename_size));
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ (*filename) = kmalloc(((*filename_size) + 1), GFP_KERNEL);
|
|
|
|
+ if (!(*filename)) {
|
|
|
|
+ printk(KERN_ERR "%s: Out of memory whilst attempting to "
|
|
|
|
+ "kmalloc [%d] bytes\n", __func__,
|
|
|
|
+ ((*filename_size) + 1));
|
|
|
|
+ rc = -ENOMEM;
|
|
|
|
+ goto out_free_unlock;
|
|
|
|
+ }
|
|
|
|
+ memcpy((*filename), &s->decrypted_filename[s->i], (*filename_size));
|
|
|
|
+ (*filename)[(*filename_size)] = '\0';
|
|
|
|
+out_free_unlock:
|
|
|
|
+ kfree(s->decrypted_filename);
|
|
|
|
+out_unlock:
|
|
|
|
+ mutex_unlock(s->tfm_mutex);
|
|
|
|
+out:
|
|
|
|
+ if (rc) {
|
|
|
|
+ (*packet_size) = 0;
|
|
|
|
+ (*filename_size) = 0;
|
|
|
|
+ (*filename) = NULL;
|
|
|
|
+ }
|
|
|
|
+ kfree(s);
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
static int
|
|
static int
|
|
ecryptfs_get_auth_tok_sig(char **sig, struct ecryptfs_auth_tok *auth_tok)
|
|
ecryptfs_get_auth_tok_sig(char **sig, struct ecryptfs_auth_tok *auth_tok)
|
|
{
|
|
{
|
|
@@ -897,30 +1460,6 @@ out:
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-ecryptfs_find_global_auth_tok_for_sig(
|
|
|
|
- struct ecryptfs_global_auth_tok **global_auth_tok,
|
|
|
|
- struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig)
|
|
|
|
-{
|
|
|
|
- struct ecryptfs_global_auth_tok *walker;
|
|
|
|
- int rc = 0;
|
|
|
|
-
|
|
|
|
- (*global_auth_tok) = NULL;
|
|
|
|
- mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
|
|
|
|
- list_for_each_entry(walker,
|
|
|
|
- &mount_crypt_stat->global_auth_tok_list,
|
|
|
|
- mount_crypt_stat_list) {
|
|
|
|
- if (memcmp(walker->sig, sig, ECRYPTFS_SIG_SIZE_HEX) == 0) {
|
|
|
|
- (*global_auth_tok) = walker;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- rc = -EINVAL;
|
|
|
|
-out:
|
|
|
|
- mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
|
|
|
|
- return rc;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* ecryptfs_verify_version
|
|
* ecryptfs_verify_version
|
|
* @version: The version number to confirm
|
|
* @version: The version number to confirm
|
|
@@ -989,43 +1528,6 @@ out:
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * ecryptfs_find_auth_tok_for_sig
|
|
|
|
- * @auth_tok: Set to the matching auth_tok; NULL if not found
|
|
|
|
- * @crypt_stat: inode crypt_stat crypto context
|
|
|
|
- * @sig: Sig of auth_tok to find
|
|
|
|
- *
|
|
|
|
- * For now, this function simply looks at the registered auth_tok's
|
|
|
|
- * linked off the mount_crypt_stat, so all the auth_toks that can be
|
|
|
|
- * used must be registered at mount time. This function could
|
|
|
|
- * potentially try a lot harder to find auth_tok's (e.g., by calling
|
|
|
|
- * out to ecryptfsd to dynamically retrieve an auth_tok object) so
|
|
|
|
- * that static registration of auth_tok's will no longer be necessary.
|
|
|
|
- *
|
|
|
|
- * Returns zero on no error; non-zero on error
|
|
|
|
- */
|
|
|
|
-static int
|
|
|
|
-ecryptfs_find_auth_tok_for_sig(
|
|
|
|
- struct ecryptfs_auth_tok **auth_tok,
|
|
|
|
- struct ecryptfs_crypt_stat *crypt_stat, char *sig)
|
|
|
|
-{
|
|
|
|
- struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
|
|
|
|
- crypt_stat->mount_crypt_stat;
|
|
|
|
- struct ecryptfs_global_auth_tok *global_auth_tok;
|
|
|
|
- int rc = 0;
|
|
|
|
-
|
|
|
|
- (*auth_tok) = NULL;
|
|
|
|
- if (ecryptfs_find_global_auth_tok_for_sig(&global_auth_tok,
|
|
|
|
- mount_crypt_stat, sig)) {
|
|
|
|
- struct key *auth_tok_key;
|
|
|
|
-
|
|
|
|
- rc = ecryptfs_keyring_auth_tok_for_sig(&auth_tok_key, auth_tok,
|
|
|
|
- sig);
|
|
|
|
- } else
|
|
|
|
- (*auth_tok) = global_auth_tok->global_auth_tok;
|
|
|
|
- return rc;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* decrypt_passphrase_encrypted_session_key - Decrypt the session key with the given auth_tok.
|
|
* decrypt_passphrase_encrypted_session_key - Decrypt the session key with the given auth_tok.
|
|
* @auth_tok: The passphrase authentication token to use to encrypt the FEK
|
|
* @auth_tok: The passphrase authentication token to use to encrypt the FEK
|
|
@@ -1256,7 +1758,8 @@ find_next_matching_auth_tok:
|
|
rc = -EINVAL;
|
|
rc = -EINVAL;
|
|
goto out_wipe_list;
|
|
goto out_wipe_list;
|
|
}
|
|
}
|
|
- ecryptfs_find_auth_tok_for_sig(&matching_auth_tok, crypt_stat,
|
|
|
|
|
|
+ ecryptfs_find_auth_tok_for_sig(&matching_auth_tok,
|
|
|
|
+ crypt_stat->mount_crypt_stat,
|
|
candidate_auth_tok_sig);
|
|
candidate_auth_tok_sig);
|
|
if (matching_auth_tok) {
|
|
if (matching_auth_tok) {
|
|
found_auth_tok = 1;
|
|
found_auth_tok = 1;
|
|
@@ -1336,7 +1839,9 @@ pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
|
|
int rc;
|
|
int rc;
|
|
|
|
|
|
rc = write_tag_66_packet(auth_tok->token.private_key.signature,
|
|
rc = write_tag_66_packet(auth_tok->token.private_key.signature,
|
|
- ecryptfs_code_for_cipher_string(crypt_stat),
|
|
|
|
|
|
+ ecryptfs_code_for_cipher_string(
|
|
|
|
+ crypt_stat->cipher,
|
|
|
|
+ crypt_stat->key_size),
|
|
crypt_stat, &payload, &payload_len);
|
|
crypt_stat, &payload, &payload_len);
|
|
if (rc) {
|
|
if (rc) {
|
|
ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n");
|
|
ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n");
|
|
@@ -1696,7 +2201,8 @@ encrypted_session_key_set:
|
|
dest[(*packet_size)++] = 0x04; /* version 4 */
|
|
dest[(*packet_size)++] = 0x04; /* version 4 */
|
|
/* TODO: Break from RFC2440 so that arbitrary ciphers can be
|
|
/* TODO: Break from RFC2440 so that arbitrary ciphers can be
|
|
* specified with strings */
|
|
* specified with strings */
|
|
- cipher_code = ecryptfs_code_for_cipher_string(crypt_stat);
|
|
|
|
|
|
+ cipher_code = ecryptfs_code_for_cipher_string(crypt_stat->cipher,
|
|
|
|
+ crypt_stat->key_size);
|
|
if (cipher_code == 0) {
|
|
if (cipher_code == 0) {
|
|
ecryptfs_printk(KERN_WARNING, "Unable to generate code for "
|
|
ecryptfs_printk(KERN_WARNING, "Unable to generate code for "
|
|
"cipher [%s]\n", crypt_stat->cipher);
|
|
"cipher [%s]\n", crypt_stat->cipher);
|