brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · cf3b58e Raw
326 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Key setup for v1 encryption policies4 *5 * Copyright 2015, 2019 Google LLC6 */7 8/*9 * This file implements compatibility functions for the original encryption10 * policy version ("v1"), including:11 *12 * - Deriving per-file encryption keys using the AES-128-ECB based KDF13 *   (rather than the new method of using HKDF-SHA512)14 *15 * - Retrieving fscrypt master keys from process-subscribed keyrings16 *   (rather than the new method of using a filesystem-level keyring)17 *18 * - Handling policies with the DIRECT_KEY flag set using a master key table19 *   (rather than the new method of implementing DIRECT_KEY with per-mode keys20 *    managed alongside the master keys in the filesystem-level keyring)21 */22 23#include <crypto/skcipher.h>24#include <crypto/utils.h>25#include <keys/user-type.h>26#include <linux/hashtable.h>27#include <linux/scatterlist.h>28 29#include "fscrypt_private.h"30 31/* Table of keys referenced by DIRECT_KEY policies */32static DEFINE_HASHTABLE(fscrypt_direct_keys, 6); /* 6 bits = 64 buckets */33static DEFINE_SPINLOCK(fscrypt_direct_keys_lock);34 35/*36 * v1 key derivation function.  This generates the derived key by encrypting the37 * master key with AES-128-ECB using the nonce as the AES key.  This provides a38 * unique derived key with sufficient entropy for each inode.  However, it's39 * nonstandard, non-extensible, doesn't evenly distribute the entropy from the40 * master key, and is trivially reversible: an attacker who compromises a41 * derived key can "decrypt" it to get back to the master key, then derive any42 * other key.  For all new code, use HKDF instead.43 *44 * The master key must be at least as long as the derived key.  If the master45 * key is longer, then only the first 'derived_keysize' bytes are used.46 */47static int derive_key_aes(const u8 *master_key,48			  const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],49			  u8 *derived_key, unsigned int derived_keysize)50{51	int res = 0;52	struct skcipher_request *req = NULL;53	DECLARE_CRYPTO_WAIT(wait);54	struct scatterlist src_sg, dst_sg;55	struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);56 57	if (IS_ERR(tfm)) {58		res = PTR_ERR(tfm);59		tfm = NULL;60		goto out;61	}62	crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);63	req = skcipher_request_alloc(tfm, GFP_KERNEL);64	if (!req) {65		res = -ENOMEM;66		goto out;67	}68	skcipher_request_set_callback(req,69			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,70			crypto_req_done, &wait);71	res = crypto_skcipher_setkey(tfm, nonce, FSCRYPT_FILE_NONCE_SIZE);72	if (res < 0)73		goto out;74 75	sg_init_one(&src_sg, master_key, derived_keysize);76	sg_init_one(&dst_sg, derived_key, derived_keysize);77	skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize,78				   NULL);79	res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);80out:81	skcipher_request_free(req);82	crypto_free_skcipher(tfm);83	return res;84}85 86/*87 * Search the current task's subscribed keyrings for a "logon" key with88 * description prefix:descriptor, and if found acquire a read lock on it and89 * return a pointer to its validated payload in *payload_ret.90 */91static struct key *92find_and_lock_process_key(const char *prefix,93			  const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE],94			  unsigned int min_keysize,95			  const struct fscrypt_key **payload_ret)96{97	char *description;98	struct key *key;99	const struct user_key_payload *ukp;100	const struct fscrypt_key *payload;101 102	description = kasprintf(GFP_KERNEL, "%s%*phN", prefix,103				FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor);104	if (!description)105		return ERR_PTR(-ENOMEM);106 107	key = request_key(&key_type_logon, description, NULL);108	kfree(description);109	if (IS_ERR(key))110		return key;111 112	down_read(&key->sem);113	ukp = user_key_payload_locked(key);114 115	if (!ukp) /* was the key revoked before we acquired its semaphore? */116		goto invalid;117 118	payload = (const struct fscrypt_key *)ukp->data;119 120	if (ukp->datalen != sizeof(struct fscrypt_key) ||121	    payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) {122		fscrypt_warn(NULL,123			     "key with description '%s' has invalid payload",124			     key->description);125		goto invalid;126	}127 128	if (payload->size < min_keysize) {129		fscrypt_warn(NULL,130			     "key with description '%s' is too short (got %u bytes, need %u+ bytes)",131			     key->description, payload->size, min_keysize);132		goto invalid;133	}134 135	*payload_ret = payload;136	return key;137 138invalid:139	up_read(&key->sem);140	key_put(key);141	return ERR_PTR(-ENOKEY);142}143 144/* Master key referenced by DIRECT_KEY policy */145struct fscrypt_direct_key {146	struct super_block		*dk_sb;147	struct hlist_node		dk_node;148	refcount_t			dk_refcount;149	const struct fscrypt_mode	*dk_mode;150	struct fscrypt_prepared_key	dk_key;151	u8				dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];152	u8				dk_raw[FSCRYPT_MAX_KEY_SIZE];153};154 155static void free_direct_key(struct fscrypt_direct_key *dk)156{157	if (dk) {158		fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key);159		kfree_sensitive(dk);160	}161}162 163void fscrypt_put_direct_key(struct fscrypt_direct_key *dk)164{165	if (!refcount_dec_and_lock(&dk->dk_refcount, &fscrypt_direct_keys_lock))166		return;167	hash_del(&dk->dk_node);168	spin_unlock(&fscrypt_direct_keys_lock);169 170	free_direct_key(dk);171}172 173/*174 * Find/insert the given key into the fscrypt_direct_keys table.  If found, it175 * is returned with elevated refcount, and 'to_insert' is freed if non-NULL.  If176 * not found, 'to_insert' is inserted and returned if it's non-NULL; otherwise177 * NULL is returned.178 */179static struct fscrypt_direct_key *180find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,181			  const u8 *raw_key,182			  const struct fscrypt_inode_info *ci)183{184	unsigned long hash_key;185	struct fscrypt_direct_key *dk;186 187	/*188	 * Careful: to avoid potentially leaking secret key bytes via timing189	 * information, we must key the hash table by descriptor rather than by190	 * raw key, and use crypto_memneq() when comparing raw keys.191	 */192 193	BUILD_BUG_ON(sizeof(hash_key) > FSCRYPT_KEY_DESCRIPTOR_SIZE);194	memcpy(&hash_key, ci->ci_policy.v1.master_key_descriptor,195	       sizeof(hash_key));196 197	spin_lock(&fscrypt_direct_keys_lock);198	hash_for_each_possible(fscrypt_direct_keys, dk, dk_node, hash_key) {199		if (memcmp(ci->ci_policy.v1.master_key_descriptor,200			   dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)201			continue;202		if (ci->ci_mode != dk->dk_mode)203			continue;204		if (!fscrypt_is_key_prepared(&dk->dk_key, ci))205			continue;206		if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize))207			continue;208		/* using existing tfm with same (descriptor, mode, raw_key) */209		refcount_inc(&dk->dk_refcount);210		spin_unlock(&fscrypt_direct_keys_lock);211		free_direct_key(to_insert);212		return dk;213	}214	if (to_insert)215		hash_add(fscrypt_direct_keys, &to_insert->dk_node, hash_key);216	spin_unlock(&fscrypt_direct_keys_lock);217	return to_insert;218}219 220/* Prepare to encrypt directly using the master key in the given mode */221static struct fscrypt_direct_key *222fscrypt_get_direct_key(const struct fscrypt_inode_info *ci, const u8 *raw_key)223{224	struct fscrypt_direct_key *dk;225	int err;226 227	/* Is there already a tfm for this key? */228	dk = find_or_insert_direct_key(NULL, raw_key, ci);229	if (dk)230		return dk;231 232	/* Nope, allocate one. */233	dk = kzalloc(sizeof(*dk), GFP_KERNEL);234	if (!dk)235		return ERR_PTR(-ENOMEM);236	dk->dk_sb = ci->ci_inode->i_sb;237	refcount_set(&dk->dk_refcount, 1);238	dk->dk_mode = ci->ci_mode;239	err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);240	if (err)241		goto err_free_dk;242	memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor,243	       FSCRYPT_KEY_DESCRIPTOR_SIZE);244	memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize);245 246	return find_or_insert_direct_key(dk, raw_key, ci);247 248err_free_dk:249	free_direct_key(dk);250	return ERR_PTR(err);251}252 253/* v1 policy, DIRECT_KEY: use the master key directly */254static int setup_v1_file_key_direct(struct fscrypt_inode_info *ci,255				    const u8 *raw_master_key)256{257	struct fscrypt_direct_key *dk;258 259	dk = fscrypt_get_direct_key(ci, raw_master_key);260	if (IS_ERR(dk))261		return PTR_ERR(dk);262	ci->ci_direct_key = dk;263	ci->ci_enc_key = dk->dk_key;264	return 0;265}266 267/* v1 policy, !DIRECT_KEY: derive the file's encryption key */268static int setup_v1_file_key_derived(struct fscrypt_inode_info *ci,269				     const u8 *raw_master_key)270{271	u8 *derived_key;272	int err;273 274	/*275	 * This cannot be a stack buffer because it will be passed to the276	 * scatterlist crypto API during derive_key_aes().277	 */278	derived_key = kmalloc(ci->ci_mode->keysize, GFP_KERNEL);279	if (!derived_key)280		return -ENOMEM;281 282	err = derive_key_aes(raw_master_key, ci->ci_nonce,283			     derived_key, ci->ci_mode->keysize);284	if (err)285		goto out;286 287	err = fscrypt_set_per_file_enc_key(ci, derived_key);288out:289	kfree_sensitive(derived_key);290	return err;291}292 293int fscrypt_setup_v1_file_key(struct fscrypt_inode_info *ci,294			      const u8 *raw_master_key)295{296	if (ci->ci_policy.v1.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)297		return setup_v1_file_key_direct(ci, raw_master_key);298	else299		return setup_v1_file_key_derived(ci, raw_master_key);300}301 302int303fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_inode_info *ci)304{305	const struct super_block *sb = ci->ci_inode->i_sb;306	struct key *key;307	const struct fscrypt_key *payload;308	int err;309 310	key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,311					ci->ci_policy.v1.master_key_descriptor,312					ci->ci_mode->keysize, &payload);313	if (key == ERR_PTR(-ENOKEY) && sb->s_cop->legacy_key_prefix) {314		key = find_and_lock_process_key(sb->s_cop->legacy_key_prefix,315						ci->ci_policy.v1.master_key_descriptor,316						ci->ci_mode->keysize, &payload);317	}318	if (IS_ERR(key))319		return PTR_ERR(key);320 321	err = fscrypt_setup_v1_file_key(ci, payload->raw);322	up_read(&key->sem);323	key_put(key);324	return err;325}326