162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/err.h>
662306a36Sopenharmony_ci#include <linux/scatterlist.h>
762306a36Sopenharmony_ci#include <linux/sched.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <crypto/aes.h>
1062306a36Sopenharmony_ci#include <crypto/skcipher.h>
1162306a36Sopenharmony_ci#include <linux/key-type.h>
1262306a36Sopenharmony_ci#include <linux/sched/mm.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <keys/ceph-type.h>
1562306a36Sopenharmony_ci#include <keys/user-type.h>
1662306a36Sopenharmony_ci#include <linux/ceph/decode.h>
1762306a36Sopenharmony_ci#include "crypto.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * Set ->key and ->tfm.  The rest of the key should be filled in before
2162306a36Sopenharmony_ci * this function is called.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistatic int set_secret(struct ceph_crypto_key *key, void *buf)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	unsigned int noio_flag;
2662306a36Sopenharmony_ci	int ret;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	key->key = NULL;
2962306a36Sopenharmony_ci	key->tfm = NULL;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	switch (key->type) {
3262306a36Sopenharmony_ci	case CEPH_CRYPTO_NONE:
3362306a36Sopenharmony_ci		return 0; /* nothing to do */
3462306a36Sopenharmony_ci	case CEPH_CRYPTO_AES:
3562306a36Sopenharmony_ci		break;
3662306a36Sopenharmony_ci	default:
3762306a36Sopenharmony_ci		return -ENOTSUPP;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (!key->len)
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	key->key = kmemdup(buf, key->len, GFP_NOIO);
4462306a36Sopenharmony_ci	if (!key->key) {
4562306a36Sopenharmony_ci		ret = -ENOMEM;
4662306a36Sopenharmony_ci		goto fail;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* crypto_alloc_sync_skcipher() allocates with GFP_KERNEL */
5062306a36Sopenharmony_ci	noio_flag = memalloc_noio_save();
5162306a36Sopenharmony_ci	key->tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
5262306a36Sopenharmony_ci	memalloc_noio_restore(noio_flag);
5362306a36Sopenharmony_ci	if (IS_ERR(key->tfm)) {
5462306a36Sopenharmony_ci		ret = PTR_ERR(key->tfm);
5562306a36Sopenharmony_ci		key->tfm = NULL;
5662306a36Sopenharmony_ci		goto fail;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ret = crypto_sync_skcipher_setkey(key->tfm, key->key, key->len);
6062306a36Sopenharmony_ci	if (ret)
6162306a36Sopenharmony_ci		goto fail;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cifail:
6662306a36Sopenharmony_ci	ceph_crypto_key_destroy(key);
6762306a36Sopenharmony_ci	return ret;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint ceph_crypto_key_clone(struct ceph_crypto_key *dst,
7162306a36Sopenharmony_ci			  const struct ceph_crypto_key *src)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	memcpy(dst, src, sizeof(struct ceph_crypto_key));
7462306a36Sopenharmony_ci	return set_secret(dst, src->key);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciint ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	if (*p + sizeof(u16) + sizeof(key->created) +
8062306a36Sopenharmony_ci	    sizeof(u16) + key->len > end)
8162306a36Sopenharmony_ci		return -ERANGE;
8262306a36Sopenharmony_ci	ceph_encode_16(p, key->type);
8362306a36Sopenharmony_ci	ceph_encode_copy(p, &key->created, sizeof(key->created));
8462306a36Sopenharmony_ci	ceph_encode_16(p, key->len);
8562306a36Sopenharmony_ci	ceph_encode_copy(p, key->key, key->len);
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciint ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int ret;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad);
9462306a36Sopenharmony_ci	key->type = ceph_decode_16(p);
9562306a36Sopenharmony_ci	ceph_decode_copy(p, &key->created, sizeof(key->created));
9662306a36Sopenharmony_ci	key->len = ceph_decode_16(p);
9762306a36Sopenharmony_ci	ceph_decode_need(p, end, key->len, bad);
9862306a36Sopenharmony_ci	ret = set_secret(key, *p);
9962306a36Sopenharmony_ci	memzero_explicit(*p, key->len);
10062306a36Sopenharmony_ci	*p += key->len;
10162306a36Sopenharmony_ci	return ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cibad:
10462306a36Sopenharmony_ci	dout("failed to decode crypto key\n");
10562306a36Sopenharmony_ci	return -EINVAL;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciint ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	int inlen = strlen(inkey);
11162306a36Sopenharmony_ci	int blen = inlen * 3 / 4;
11262306a36Sopenharmony_ci	void *buf, *p;
11362306a36Sopenharmony_ci	int ret;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	dout("crypto_key_unarmor %s\n", inkey);
11662306a36Sopenharmony_ci	buf = kmalloc(blen, GFP_NOFS);
11762306a36Sopenharmony_ci	if (!buf)
11862306a36Sopenharmony_ci		return -ENOMEM;
11962306a36Sopenharmony_ci	blen = ceph_unarmor(buf, inkey, inkey+inlen);
12062306a36Sopenharmony_ci	if (blen < 0) {
12162306a36Sopenharmony_ci		kfree(buf);
12262306a36Sopenharmony_ci		return blen;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	p = buf;
12662306a36Sopenharmony_ci	ret = ceph_crypto_key_decode(key, &p, p + blen);
12762306a36Sopenharmony_ci	kfree(buf);
12862306a36Sopenharmony_ci	if (ret)
12962306a36Sopenharmony_ci		return ret;
13062306a36Sopenharmony_ci	dout("crypto_key_unarmor key %p type %d len %d\n", key,
13162306a36Sopenharmony_ci	     key->type, key->len);
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_civoid ceph_crypto_key_destroy(struct ceph_crypto_key *key)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	if (key) {
13862306a36Sopenharmony_ci		kfree_sensitive(key->key);
13962306a36Sopenharmony_ci		key->key = NULL;
14062306a36Sopenharmony_ci		if (key->tfm) {
14162306a36Sopenharmony_ci			crypto_free_sync_skcipher(key->tfm);
14262306a36Sopenharmony_ci			key->tfm = NULL;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic const u8 *aes_iv = (u8 *)CEPH_AES_IV;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Should be used for buffers allocated with kvmalloc().
15162306a36Sopenharmony_ci * Currently these are encrypt out-buffer (ceph_buffer) and decrypt
15262306a36Sopenharmony_ci * in-buffer (msg front).
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * Dispose of @sgt with teardown_sgtable().
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
15762306a36Sopenharmony_ci * in cases where a single sg is sufficient.  No attempt to reduce the
15862306a36Sopenharmony_ci * number of sgs by squeezing physically contiguous pages together is
15962306a36Sopenharmony_ci * made though, for simplicity.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
16262306a36Sopenharmony_ci			 const void *buf, unsigned int buf_len)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct scatterlist *sg;
16562306a36Sopenharmony_ci	const bool is_vmalloc = is_vmalloc_addr(buf);
16662306a36Sopenharmony_ci	unsigned int off = offset_in_page(buf);
16762306a36Sopenharmony_ci	unsigned int chunk_cnt = 1;
16862306a36Sopenharmony_ci	unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
16962306a36Sopenharmony_ci	int i;
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (buf_len == 0) {
17362306a36Sopenharmony_ci		memset(sgt, 0, sizeof(*sgt));
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (is_vmalloc) {
17862306a36Sopenharmony_ci		chunk_cnt = chunk_len >> PAGE_SHIFT;
17962306a36Sopenharmony_ci		chunk_len = PAGE_SIZE;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (chunk_cnt > 1) {
18362306a36Sopenharmony_ci		ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
18462306a36Sopenharmony_ci		if (ret)
18562306a36Sopenharmony_ci			return ret;
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		WARN_ON(chunk_cnt != 1);
18862306a36Sopenharmony_ci		sg_init_table(prealloc_sg, 1);
18962306a36Sopenharmony_ci		sgt->sgl = prealloc_sg;
19062306a36Sopenharmony_ci		sgt->nents = sgt->orig_nents = 1;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
19462306a36Sopenharmony_ci		struct page *page;
19562306a36Sopenharmony_ci		unsigned int len = min(chunk_len - off, buf_len);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		if (is_vmalloc)
19862306a36Sopenharmony_ci			page = vmalloc_to_page(buf);
19962306a36Sopenharmony_ci		else
20062306a36Sopenharmony_ci			page = virt_to_page(buf);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		sg_set_page(sg, page, len, off);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		off = 0;
20562306a36Sopenharmony_ci		buf += len;
20662306a36Sopenharmony_ci		buf_len -= len;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	WARN_ON(buf_len != 0);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void teardown_sgtable(struct sg_table *sgt)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (sgt->orig_nents > 1)
21662306a36Sopenharmony_ci		sg_free_table(sgt);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt,
22062306a36Sopenharmony_ci			  void *buf, int buf_len, int in_len, int *pout_len)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm);
22362306a36Sopenharmony_ci	struct sg_table sgt;
22462306a36Sopenharmony_ci	struct scatterlist prealloc_sg;
22562306a36Sopenharmony_ci	char iv[AES_BLOCK_SIZE] __aligned(8);
22662306a36Sopenharmony_ci	int pad_byte = AES_BLOCK_SIZE - (in_len & (AES_BLOCK_SIZE - 1));
22762306a36Sopenharmony_ci	int crypt_len = encrypt ? in_len + pad_byte : in_len;
22862306a36Sopenharmony_ci	int ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	WARN_ON(crypt_len > buf_len);
23162306a36Sopenharmony_ci	if (encrypt)
23262306a36Sopenharmony_ci		memset(buf + in_len, pad_byte, pad_byte);
23362306a36Sopenharmony_ci	ret = setup_sgtable(&sgt, &prealloc_sg, buf, crypt_len);
23462306a36Sopenharmony_ci	if (ret)
23562306a36Sopenharmony_ci		return ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	memcpy(iv, aes_iv, AES_BLOCK_SIZE);
23862306a36Sopenharmony_ci	skcipher_request_set_sync_tfm(req, key->tfm);
23962306a36Sopenharmony_ci	skcipher_request_set_callback(req, 0, NULL, NULL);
24062306a36Sopenharmony_ci	skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/*
24362306a36Sopenharmony_ci	print_hex_dump(KERN_ERR, "key: ", DUMP_PREFIX_NONE, 16, 1,
24462306a36Sopenharmony_ci		       key->key, key->len, 1);
24562306a36Sopenharmony_ci	print_hex_dump(KERN_ERR, " in: ", DUMP_PREFIX_NONE, 16, 1,
24662306a36Sopenharmony_ci		       buf, crypt_len, 1);
24762306a36Sopenharmony_ci	*/
24862306a36Sopenharmony_ci	if (encrypt)
24962306a36Sopenharmony_ci		ret = crypto_skcipher_encrypt(req);
25062306a36Sopenharmony_ci	else
25162306a36Sopenharmony_ci		ret = crypto_skcipher_decrypt(req);
25262306a36Sopenharmony_ci	skcipher_request_zero(req);
25362306a36Sopenharmony_ci	if (ret) {
25462306a36Sopenharmony_ci		pr_err("%s %scrypt failed: %d\n", __func__,
25562306a36Sopenharmony_ci		       encrypt ? "en" : "de", ret);
25662306a36Sopenharmony_ci		goto out_sgt;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	/*
25962306a36Sopenharmony_ci	print_hex_dump(KERN_ERR, "out: ", DUMP_PREFIX_NONE, 16, 1,
26062306a36Sopenharmony_ci		       buf, crypt_len, 1);
26162306a36Sopenharmony_ci	*/
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (encrypt) {
26462306a36Sopenharmony_ci		*pout_len = crypt_len;
26562306a36Sopenharmony_ci	} else {
26662306a36Sopenharmony_ci		pad_byte = *(char *)(buf + in_len - 1);
26762306a36Sopenharmony_ci		if (pad_byte > 0 && pad_byte <= AES_BLOCK_SIZE &&
26862306a36Sopenharmony_ci		    in_len >= pad_byte) {
26962306a36Sopenharmony_ci			*pout_len = in_len - pad_byte;
27062306a36Sopenharmony_ci		} else {
27162306a36Sopenharmony_ci			pr_err("%s got bad padding %d on in_len %d\n",
27262306a36Sopenharmony_ci			       __func__, pad_byte, in_len);
27362306a36Sopenharmony_ci			ret = -EPERM;
27462306a36Sopenharmony_ci			goto out_sgt;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ciout_sgt:
27962306a36Sopenharmony_ci	teardown_sgtable(&sgt);
28062306a36Sopenharmony_ci	return ret;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
28462306a36Sopenharmony_ci	       void *buf, int buf_len, int in_len, int *pout_len)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	switch (key->type) {
28762306a36Sopenharmony_ci	case CEPH_CRYPTO_NONE:
28862306a36Sopenharmony_ci		*pout_len = in_len;
28962306a36Sopenharmony_ci		return 0;
29062306a36Sopenharmony_ci	case CEPH_CRYPTO_AES:
29162306a36Sopenharmony_ci		return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len,
29262306a36Sopenharmony_ci				      pout_len);
29362306a36Sopenharmony_ci	default:
29462306a36Sopenharmony_ci		return -ENOTSUPP;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int ceph_key_preparse(struct key_preparsed_payload *prep)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct ceph_crypto_key *ckey;
30162306a36Sopenharmony_ci	size_t datalen = prep->datalen;
30262306a36Sopenharmony_ci	int ret;
30362306a36Sopenharmony_ci	void *p;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ret = -EINVAL;
30662306a36Sopenharmony_ci	if (datalen <= 0 || datalen > 32767 || !prep->data)
30762306a36Sopenharmony_ci		goto err;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	ret = -ENOMEM;
31062306a36Sopenharmony_ci	ckey = kmalloc(sizeof(*ckey), GFP_KERNEL);
31162306a36Sopenharmony_ci	if (!ckey)
31262306a36Sopenharmony_ci		goto err;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* TODO ceph_crypto_key_decode should really take const input */
31562306a36Sopenharmony_ci	p = (void *)prep->data;
31662306a36Sopenharmony_ci	ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen);
31762306a36Sopenharmony_ci	if (ret < 0)
31862306a36Sopenharmony_ci		goto err_ckey;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	prep->payload.data[0] = ckey;
32162306a36Sopenharmony_ci	prep->quotalen = datalen;
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cierr_ckey:
32562306a36Sopenharmony_ci	kfree(ckey);
32662306a36Sopenharmony_cierr:
32762306a36Sopenharmony_ci	return ret;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void ceph_key_free_preparse(struct key_preparsed_payload *prep)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct ceph_crypto_key *ckey = prep->payload.data[0];
33362306a36Sopenharmony_ci	ceph_crypto_key_destroy(ckey);
33462306a36Sopenharmony_ci	kfree(ckey);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void ceph_key_destroy(struct key *key)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct ceph_crypto_key *ckey = key->payload.data[0];
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ceph_crypto_key_destroy(ckey);
34262306a36Sopenharmony_ci	kfree(ckey);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistruct key_type key_type_ceph = {
34662306a36Sopenharmony_ci	.name		= "ceph",
34762306a36Sopenharmony_ci	.preparse	= ceph_key_preparse,
34862306a36Sopenharmony_ci	.free_preparse	= ceph_key_free_preparse,
34962306a36Sopenharmony_ci	.instantiate	= generic_key_instantiate,
35062306a36Sopenharmony_ci	.destroy	= ceph_key_destroy,
35162306a36Sopenharmony_ci};
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ciint __init ceph_crypto_init(void)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	return register_key_type(&key_type_ceph);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_civoid ceph_crypto_shutdown(void)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	unregister_key_type(&key_type_ceph);
36162306a36Sopenharmony_ci}
362