Release 4.10 fs/ecryptfs/keystore.c
/**
* eCryptfs: Linux filesystem encryption layer
* In-kernel key management code. Includes functions to parse and
* write authentication token-related packets with the underlying
* file.
*
* Copyright (C) 2004-2006 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
* Trevor S. Highland <trevor.highland@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <crypto/hash.h>
#include <crypto/skcipher.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/key.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include "ecryptfs_kernel.h"
/**
* request_key returned an error instead of a valid key address;
* determine the type of error, make appropriate log entries, and
* return an error code.
*/
static int process_request_key_err(long err_code)
{
int rc = 0;
switch (err_code) {
case -ENOKEY:
ecryptfs_printk(KERN_WARNING, "No key\n");
rc = -ENOENT;
break;
case -EKEYEXPIRED:
ecryptfs_printk(KERN_WARNING, "Key expired\n");
rc = -ETIME;
break;
case -EKEYREVOKED:
ecryptfs_printk(KERN_WARNING, "Key revoked\n");
rc = -EINVAL;
break;
default:
ecryptfs_printk(KERN_WARNING, "Unknown error code: "
"[0x%.16lx]\n", err_code);
rc = -EINVAL;
}
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 86 | 95.56% | 2 | 50.00% |
eric sandeen | eric sandeen | 3 | 3.33% | 1 | 25.00% |
joe perches | joe perches | 1 | 1.11% | 1 | 25.00% |
| Total | 90 | 100.00% | 4 | 100.00% |
static int process_find_global_auth_tok_for_sig_err(int err_code)
{
int rc = err_code;
switch (err_code) {
case -ENOENT:
ecryptfs_printk(KERN_WARNING, "Missing auth tok\n");
break;
case -EINVAL:
ecryptfs_printk(KERN_WARNING, "Invalid auth tok\n");
break;
default:
rc = process_request_key_err(err_code);
break;
}
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
roberto sassu | roberto sassu | 56 | 100.00% | 1 | 100.00% |
| Total | 56 | 100.00% | 1 | 100.00% |
/**
* ecryptfs_parse_packet_length
* @data: Pointer to memory containing length at offset
* @size: This function writes the decoded size to this memory
* address; zero on error
* @length_size: The number of bytes occupied by the encoded length
*
* Returns zero on success; non-zero on error
*/
int ecryptfs_parse_packet_length(unsigned char *data, size_t *size,
size_t *length_size)
{
int rc = 0;
(*length_size) = 0;
(*size) = 0;
if (data[0] < 192) {
/* One-byte length */
(*size) = data[0];
(*length_size) = 1;
} else if (data[0] < 224) {
/* Two-byte length */
(*size) = (data[0] - 192) * 256;
(*size) += data[1] + 192;
(*length_size) = 2;
} else if (data[0] == 255) {
/* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */
ecryptfs_printk(KERN_ERR, "Five-byte packet length not "
"supported\n");
rc = -EINVAL;
goto out;
} else {
ecryptfs_printk(KERN_ERR, "Error parsing packet length\n");
rc = -EINVAL;
goto out;
}
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 165 | 99.40% | 2 | 66.67% |
tyler hicks | tyler hicks | 1 | 0.60% | 1 | 33.33% |
| Total | 166 | 100.00% | 3 | 100.00% |
/**
* ecryptfs_write_packet_length
* @dest: The byte array target into which to write the length. Must
* have at least ECRYPTFS_MAX_PKT_LEN_SIZE bytes allocated.
* @size: The length to write.
* @packet_size_length: The number of bytes used to encode the packet
* length is written to this address.
*
* Returns zero on success; non-zero on error.
*/
int ecryptfs_write_packet_length(char *dest, size_t size,
size_t *packet_size_length)
{
int rc = 0;
if (size < 192) {
dest[0] = size;
(*packet_size_length) = 1;
} else if (size < 65536) {
dest[0] = (((size - 192) / 256) + 192);
dest[1] = ((size - 192) % 256);
(*packet_size_length) = 2;
} else {
/* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */
rc = -EINVAL;
ecryptfs_printk(KERN_WARNING,
"Unsupported packet size: [%zd]\n", size);
}
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 112 | 98.25% | 2 | 50.00% |
tyler hicks | tyler hicks | 2 | 1.75% | 2 | 50.00% |
| Total | 114 | 100.00% | 4 | 100.00% |
static int
write_tag_64_packet(char *signature, struct ecryptfs_session_key *session_key,
char **packet, size_t *packet_len)
{
size_t i = 0;
size_t data_len;
size_t packet_size_len;
char *message;
int rc;
/*
* ***** TAG 64 Packet Format *****
* | Content Type | 1 byte |
* | Key Identifier Size | 1 or 2 bytes |
* | Key Identifier | arbitrary |
* | Encrypted File Encryption Key Size | 1 or 2 bytes |
* | Encrypted File Encryption Key | arbitrary |
*/
data_len = (5 + ECRYPTFS_SIG_SIZE_HEX
+ session_key->encrypted_key_size);
*packet = kmalloc(data_len, GFP_KERNEL);
message = *packet;
if (!message) {
ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
rc = -ENOMEM;
goto out;
}
message[i++] = ECRYPTFS_TAG_64_PACKET_TYPE;
rc = ecryptfs_write_packet_length(&message[i], ECRYPTFS_SIG_SIZE_HEX,
&packet_size_len);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error generating tag 64 packet "
"header; cannot generate packet length\n");
goto out;
}
i += packet_size_len;
memcpy(&message[i], signature, ECRYPTFS_SIG_SIZE_HEX);
i += ECRYPTFS_SIG_SIZE_HEX;
rc = ecryptfs_write_packet_length(&message[i],
session_key->encrypted_key_size,
&packet_size_len);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error generating tag 64 packet "
"header; cannot generate packet length\n");
goto out;
}
i += packet_size_len;
memcpy(&message[i], session_key->encrypted_key,
session_key->encrypted_key_size);
i += session_key->encrypted_key_size;
*packet_len = i;
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 226 | 100.00% | 3 | 100.00% |
| Total | 226 | 100.00% | 3 | 100.00% |
static int
parse_tag_65_packet(struct ecryptfs_session_key *session_key, u8 *cipher_code,
struct ecryptfs_message *msg)
{
size_t i = 0;
char *data;
size_t data_len;
size_t m_size;
size_t message_len;
u16 checksum = 0;
u16 expected_checksum = 0;
int rc;
/*
* ***** TAG 65 Packet Format *****
* | Content Type | 1 byte |
* | Status Indicator | 1 byte |
* | File Encryption Key Size | 1 or 2 bytes |
* | File Encryption Key | arbitrary |
*/
message_len = msg->data_len;
data = msg->data;
if (message_len < 4) {
rc = -EIO;
goto out;
}
if (data[i++] != ECRYPTFS_TAG_65_PACKET_TYPE) {
ecryptfs_printk(KERN_ERR, "Type should be ECRYPTFS_TAG_65\n");
rc = -EIO;
goto out;
}
if (data[i++]) {
ecryptfs_printk(KERN_ERR, "Status indicator has non-zero value "
"[%d]\n", data[i-1]);
rc = -EIO;
goto out;
}
rc = ecryptfs_parse_packet_length(&data[i], &m_size, &data_len);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
"rc = [%d]\n", rc);
goto out;
}
i += data_len;
if (message_len < (i + m_size)) {
ecryptfs_printk(KERN_ERR, "The message received from ecryptfsd "
"is shorter than expected\n");
rc = -EIO;
goto out;
}
if (m_size < 3) {
ecryptfs_printk(KERN_ERR,
"The decrypted key is not long enough to "
"include a cipher code and checksum\n");
rc = -EIO;
goto out;
}
*cipher_code = data[i++];
/* The decrypted key includes 1 byte cipher code and 2 byte checksum */
session_key->decrypted_key_size = m_size - 3;
if (session_key->decrypted_key_size > ECRYPTFS_MAX_KEY_BYTES) {
ecryptfs_printk(KERN_ERR, "key_size [%d] larger than "
"the maximum key size [%d]\n",
session_key->decrypted_key_size,
ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
rc = -EIO;
goto out;
}
memcpy(session_key->decrypted_key, &data[i],
session_key->decrypted_key_size);
i += session_key->decrypted_key_size;
expected_checksum += (unsigned char)(data[i++]) << 8;
expected_checksum += (unsigned char)(data[i++]);
for (i = 0; i < session_key->decrypted_key_size; i++)
checksum += session_key->decrypted_key[i];
if (expected_checksum != checksum) {
ecryptfs_printk(KERN_ERR, "Invalid checksum for file "
"encryption key; expected [%x]; calculated "
"[%x]\n", expected_checksum, checksum);
rc = -EIO;
}
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 387 | 99.23% | 3 | 60.00% |
tyler hicks | tyler hicks | 2 | 0.51% | 1 | 20.00% |
trevor highland | trevor highland | 1 | 0.26% | 1 | 20.00% |
| Total | 390 | 100.00% | 5 | 100.00% |
static int
write_tag_66_packet(char *signature, u8 cipher_code,
struct ecryptfs_crypt_stat *crypt_stat, char **packet,
size_t *packet_len)
{
size_t i = 0;
size_t j;
size_t data_len;
size_t checksum = 0;
size_t packet_size_len;
char *message;
int rc;
/*
* ***** TAG 66 Packet Format *****
* | Content Type | 1 byte |
* | Key Identifier Size | 1 or 2 bytes |
* | Key Identifier | arbitrary |
* | File Encryption Key Size | 1 or 2 bytes |
* | File Encryption Key | arbitrary |
*/
data_len = (5 + ECRYPTFS_SIG_SIZE_HEX + crypt_stat->key_size);
*packet = kmalloc(data_len, GFP_KERNEL);
message = *packet;
if (!message) {
ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
rc = -ENOMEM;
goto out;
}
message[i++] = ECRYPTFS_TAG_66_PACKET_TYPE;
rc = ecryptfs_write_packet_length(&message[i], ECRYPTFS_SIG_SIZE_HEX,
&packet_size_len);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet "
"header; cannot generate packet length\n");
goto out;
}
i += packet_size_len;
memcpy(&message[i], signature, ECRYPTFS_SIG_SIZE_HEX);
i += ECRYPTFS_SIG_SIZE_HEX;
/* The encrypted key includes 1 byte cipher code and 2 byte checksum */
rc = ecryptfs_write_packet_length(&message[i], crypt_stat->key_size + 3,
&packet_size_len);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet "
"header; cannot generate packet length\n");
goto out;
}
i += packet_size_len;
message[i++] = cipher_code;
memcpy(&message[i], crypt_stat->key, crypt_stat->key_size);
i += crypt_stat->key_size;
for (j = 0; j < crypt_stat->key_size; j++)
checksum += crypt_stat->key[j];
message[i++] = (checksum / 256) % 256;
message[i++] = (checksum % 256);
*packet_len = i;
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 297 | 99.66% | 3 | 75.00% |
trevor highland | trevor highland | 1 | 0.34% | 1 | 25.00% |
| Total | 298 | 100.00% | 4 | 100.00% |
static int
parse_tag_67_packet(struct ecryptfs_key_record *key_rec,
struct ecryptfs_message *msg)
{
size_t i = 0;
char *data;
size_t data_len;
size_t message_len;
int rc;
/*
* ***** TAG 65 Packet Format *****
* | Content Type | 1 byte |
* | Status Indicator | 1 byte |
* | Encrypted File Encryption Key Size | 1 or 2 bytes |
* | Encrypted File Encryption Key | arbitrary |
*/
message_len = msg->data_len;
data = msg->data;
/* verify that everything through the encrypted FEK size is present */
if (message_len < 4) {
rc = -EIO;
printk(KERN_ERR "%s: message_len is [%zd]; minimum acceptable "
"message length is [%d]\n", __func__, message_len, 4);
goto out;
}
if (data[i++] != ECRYPTFS_TAG_67_PACKET_TYPE) {
rc = -EIO;
printk(KERN_ERR "%s: Type should be ECRYPTFS_TAG_67\n",
__func__);
goto out;
}
if (data[i++]) {
rc = -EIO;
printk(KERN_ERR "%s: Status indicator has non zero "
"value [%d]\n", __func__, data[i-1]);
goto out;
}
rc = ecryptfs_parse_packet_length(&data[i], &key_rec->enc_key_size,
&data_len);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
"rc = [%d]\n", rc);
goto out;
}
i += data_len;
if (message_len < (i + key_rec->enc_key_size)) {
rc = -EIO;
printk(KERN_ERR "%s: message_len [%zd]; max len is [%zd]\n",
__func__, message_len, (i + key_rec->enc_key_size));
goto out;
}
if (key_rec->enc_key_size > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
rc = -EIO;
printk(KERN_ERR "%s: Encrypted key_size [%zd] larger than "
"the maximum key size [%d]\n", __func__,
key_rec->enc_key_size,
ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
goto out;
}
memcpy(key_rec->enc_key, &data[i], key_rec->enc_key_size);
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 276 | 100.00% | 5 | 100.00% |
| Total | 276 | 100.00% | 5 | 100.00% |
/**
* ecryptfs_verify_version
* @version: The version number to confirm
*
* Returns zero on good version; non-zero otherwise
*/
static int ecryptfs_verify_version(u16 version)
{
int rc = 0;
unsigned char major;
unsigned char minor;
major = ((version >> 8) & 0xFF);
minor = (version & 0xFF);
if (major != ECRYPTFS_VERSION_MAJOR) {
ecryptfs_printk(KERN_ERR, "Major version number mismatch. "
"Expected [%d]; got [%d]\n",
ECRYPTFS_VERSION_MAJOR, major);
rc = -EINVAL;
goto out;
}
if (minor != ECRYPTFS_VERSION_MINOR) {
ecryptfs_printk(KERN_ERR, "Minor version number mismatch. "
"Expected [%d]; got [%d]\n",
ECRYPTFS_VERSION_MINOR, minor);
rc = -EINVAL;
goto out;
}
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
roberto sassu | roberto sassu | 103 | 100.00% | 1 | 100.00% |
| Total | 103 | 100.00% | 1 | 100.00% |
/**
* ecryptfs_verify_auth_tok_from_key
* @auth_tok_key: key containing the authentication token
* @auth_tok: authentication token
*
* Returns zero on valid auth tok; -EINVAL otherwise
*/
static int
ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key,
struct ecryptfs_auth_tok **auth_tok)
{
int rc = 0;
(*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key);
if (ecryptfs_verify_version((*auth_tok)->version)) {
printk(KERN_ERR "Data structure version mismatch. Userspace "
"tools must match eCryptfs kernel module with major "
"version [%d] and minor version [%d]\n",
ECRYPTFS_VERSION_MAJOR, ECRYPTFS_VERSION_MINOR);
rc = -EINVAL;
goto out;
}
if ((*auth_tok)->token_type != ECRYPTFS_PASSWORD
&& (*auth_tok)->token_type != ECRYPTFS_PRIVATE_KEY) {
printk(KERN_ERR "Invalid auth_tok structure "
"returned from key query\n");
rc = -EINVAL;
goto out;
}
out:
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
roberto sassu | roberto sassu | 108 | 100.00% | 1 | 100.00% |
| Total | 108 | 100.00% | 1 | 100.00% |
static int
ecryptfs_find_global_auth_tok_for_sig(
struct key **auth_tok_key,
struct ecryptfs_auth_tok **auth_tok,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig)
{
struct ecryptfs_global_auth_tok *walker;
int rc = 0;
(*auth_tok_key) = NULL;
(*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))
continue;
if (walker->flags & ECRYPTFS_AUTH_TOK_INVALID) {
rc = -EINVAL;
goto out;
}
rc = key_validate(walker->global_auth_tok_key);
if (rc) {
if (rc == -EKEYEXPIRED)
goto out;
goto out_invalid_auth_tok;
}
down_write(&(walker->global_auth_tok_key->sem));
rc = ecryptfs_verify_auth_tok_from_key(
walker->global_auth_tok_key, auth_tok);
if (rc)
goto out_invalid_auth_tok_unlock;
(*auth_tok_key) = walker->global_auth_tok_key;
key_get(*auth_tok_key);
goto out;
}
rc = -ENOENT;
goto out;
out_invalid_auth_tok_unlock:
up_write(&(walker->global_auth_tok_key->sem));
out_invalid_auth_tok:
printk(KERN_WARNING "Invalidating auth tok with sig = [%s]\n", sig);
walker->flags |= ECRYPTFS_AUTH_TOK_INVALID;
key_put(walker->global_auth_tok_key);
walker->global_auth_tok_key = NULL;
out:
mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
roberto sassu | roberto sassu | 135 | 56.02% | 2 | 40.00% |
michael halcrow | michael halcrow | 93 | 38.59% | 2 | 40.00% |
tyler hicks | tyler hicks | 13 | 5.39% | 1 | 20.00% |
| Total | 241 | 100.00% | 5 | 100.00% |
/**
* 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 key **auth_tok_key,
struct ecryptfs_auth_tok **auth_tok,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
char *sig)
{
int rc = 0;
rc = ecryptfs_find_global_auth_tok_for_sig(auth_tok_key, auth_tok,
mount_crypt_stat, sig);
if (rc == -ENOENT) {
/* if the flag ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY is set in the
* mount_crypt_stat structure, we prevent to use auth toks that
* are not inserted through the ecryptfs_add_global_auth_tok
* function.
*/
if (mount_crypt_stat->flags
& ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY)
return -EINVAL;
rc = ecryptfs_keyring_auth_tok_for_sig(auth_tok_key, auth_tok,
sig);
}
return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael halcrow | michael halcrow | 51 | 62.96% | 1 | 25.00% |
roberto sassu | roberto sassu | 30 | 37.04% | 3 | 75.00% |
| Total | 81 | 100.00% | 4 | 100.00% |
/**
* 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[2];
struct scatterlist dst_sg[2];
struct crypto_skcipher *skcipher_tfm;
struct skcipher_request *skcipher_req;
char iv[ECRYPTFS_MAX_IV_BYTES];
char hash[ECRYPTFS_TAG_70_DIGEST_SIZE];
char tmp_hash[ECRYPTFS_TAG_70_DIGEST_SIZE];
struct crypto_shash *hash_tfm;
struct shash_desc *hash_desc;
};
/**
* 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;
struct key *auth_tok_key = NULL;
int rc = 0;
s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
printk(KERN_ERR "%s: Out of memory whilst trying to kmalloc "
"[%zd] bytes of kernel memory\n", __func__, sizeof(*s));
return -ENOMEM;
}
(*packet_size) = 0;
rc = ecryptfs_find_auth_tok_for_sig(
&auth_tok_key,
&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;
}
rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(
&s->skcipher_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_skcipher_blocksize(s->skcipher_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 = (ECRYPTFS_TAG_70_MAX_METADATA_SIZE
+ 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 [%zd] bytes to write; only "
"[%zd] available\n", __func__, s->max_packet_size,
(*remaining_bytes));
rc = -EINVAL;
goto out_unlock;
}
s->skcipher_req = skcipher_request_alloc(s->skcipher_tfm, GFP_KERNEL);
if (!s->skcipher_req) {
printk(KERN_ERR "%s: Out of kernel memory whilst attempting to "
"skcipher_request_alloc for %s\n", __func__,
crypto_skcipher_driver_name(s->skcipher_tfm));
rc = -ENOMEM;
goto out_unlock;
}
skcipher_request_set_callback(s->skcipher_req,
CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
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;
}
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 [%zd]\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;
/* TODO: Support other key modules than passphrase for
* filename encryption */
if (s->auth_tok->token_type != ECRYPTFS_PASSWORD) {
rc = -EOPNOTSUPP;
printk(KERN_INFO "%s: Filename encryption only supports "
"password tokens\n", __func__);
goto out_free_unlock;
}
s->hash_tfm =