2516 lines · 78.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * 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> */ #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;} 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;} /** * 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;} /** * 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;} static intwrite_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;} static intparse_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;}  static intwrite_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 |	 *         | Cipher Code              | 1 byte       |	 *         | File Encryption Key      | arbitrary    |	 *         | Checksum                 | 2 bytes      |	 */	data_len = (8 + 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;} static intparse_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 67 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;} /** * 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;} /** * 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 if the payload is invalid; or * -EKEYREVOKED if the key was revoked before we acquired its semaphore. */static intecryptfs_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 (IS_ERR(*auth_tok)) {		rc = PTR_ERR(*auth_tok);		*auth_tok = NULL;		goto out;	} 	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;} static intecryptfs_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;

Lines 1–500 of 2516.