13d14c5d2SYehuda Sadeh 23d14c5d2SYehuda Sadeh #include <linux/ceph/ceph_debug.h> 33d14c5d2SYehuda Sadeh 43d14c5d2SYehuda Sadeh #include <linux/err.h> 53d14c5d2SYehuda Sadeh #include <linux/scatterlist.h> 67fea24c6SIlya Dryomov #include <linux/sched.h> 73d14c5d2SYehuda Sadeh #include <linux/slab.h> 8e59dd982SHerbert Xu #include <crypto/aes.h> 9e59dd982SHerbert Xu #include <crypto/skcipher.h> 104b2a58abSTommi Virtanen #include <linux/key-type.h> 113d14c5d2SYehuda Sadeh 124b2a58abSTommi Virtanen #include <keys/ceph-type.h> 137c3bec0aSDavid Howells #include <keys/user-type.h> 143d14c5d2SYehuda Sadeh #include <linux/ceph/decode.h> 153d14c5d2SYehuda Sadeh #include "crypto.h" 163d14c5d2SYehuda Sadeh 177af3ea18SIlya Dryomov /* 187af3ea18SIlya Dryomov * Set ->key and ->tfm. The rest of the key should be filled in before 197af3ea18SIlya Dryomov * this function is called. 207af3ea18SIlya Dryomov */ 217af3ea18SIlya Dryomov static int set_secret(struct ceph_crypto_key *key, void *buf) 227af3ea18SIlya Dryomov { 237af3ea18SIlya Dryomov unsigned int noio_flag; 247af3ea18SIlya Dryomov int ret; 257af3ea18SIlya Dryomov 267af3ea18SIlya Dryomov key->key = NULL; 277af3ea18SIlya Dryomov key->tfm = NULL; 287af3ea18SIlya Dryomov 297af3ea18SIlya Dryomov switch (key->type) { 307af3ea18SIlya Dryomov case CEPH_CRYPTO_NONE: 317af3ea18SIlya Dryomov return 0; /* nothing to do */ 327af3ea18SIlya Dryomov case CEPH_CRYPTO_AES: 337af3ea18SIlya Dryomov break; 347af3ea18SIlya Dryomov default: 357af3ea18SIlya Dryomov return -ENOTSUPP; 367af3ea18SIlya Dryomov } 377af3ea18SIlya Dryomov 387af3ea18SIlya Dryomov WARN_ON(!key->len); 397af3ea18SIlya Dryomov key->key = kmemdup(buf, key->len, GFP_NOIO); 407af3ea18SIlya Dryomov if (!key->key) { 417af3ea18SIlya Dryomov ret = -ENOMEM; 427af3ea18SIlya Dryomov goto fail; 437af3ea18SIlya Dryomov } 447af3ea18SIlya Dryomov 457af3ea18SIlya Dryomov /* crypto_alloc_skcipher() allocates with GFP_KERNEL */ 467af3ea18SIlya Dryomov noio_flag = memalloc_noio_save(); 477af3ea18SIlya Dryomov key->tfm = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); 487af3ea18SIlya Dryomov memalloc_noio_restore(noio_flag); 497af3ea18SIlya Dryomov if (IS_ERR(key->tfm)) { 507af3ea18SIlya Dryomov ret = PTR_ERR(key->tfm); 517af3ea18SIlya Dryomov key->tfm = NULL; 527af3ea18SIlya Dryomov goto fail; 537af3ea18SIlya Dryomov } 547af3ea18SIlya Dryomov 557af3ea18SIlya Dryomov ret = crypto_skcipher_setkey(key->tfm, key->key, key->len); 567af3ea18SIlya Dryomov if (ret) 577af3ea18SIlya Dryomov goto fail; 587af3ea18SIlya Dryomov 597af3ea18SIlya Dryomov return 0; 607af3ea18SIlya Dryomov 617af3ea18SIlya Dryomov fail: 627af3ea18SIlya Dryomov ceph_crypto_key_destroy(key); 637af3ea18SIlya Dryomov return ret; 647af3ea18SIlya Dryomov } 657af3ea18SIlya Dryomov 668323c3aaSTommi Virtanen int ceph_crypto_key_clone(struct ceph_crypto_key *dst, 678323c3aaSTommi Virtanen const struct ceph_crypto_key *src) 688323c3aaSTommi Virtanen { 698323c3aaSTommi Virtanen memcpy(dst, src, sizeof(struct ceph_crypto_key)); 707af3ea18SIlya Dryomov return set_secret(dst, src->key); 718323c3aaSTommi Virtanen } 728323c3aaSTommi Virtanen 733d14c5d2SYehuda Sadeh int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end) 743d14c5d2SYehuda Sadeh { 753d14c5d2SYehuda Sadeh if (*p + sizeof(u16) + sizeof(key->created) + 763d14c5d2SYehuda Sadeh sizeof(u16) + key->len > end) 773d14c5d2SYehuda Sadeh return -ERANGE; 783d14c5d2SYehuda Sadeh ceph_encode_16(p, key->type); 793d14c5d2SYehuda Sadeh ceph_encode_copy(p, &key->created, sizeof(key->created)); 803d14c5d2SYehuda Sadeh ceph_encode_16(p, key->len); 813d14c5d2SYehuda Sadeh ceph_encode_copy(p, key->key, key->len); 823d14c5d2SYehuda Sadeh return 0; 833d14c5d2SYehuda Sadeh } 843d14c5d2SYehuda Sadeh 853d14c5d2SYehuda Sadeh int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end) 863d14c5d2SYehuda Sadeh { 877af3ea18SIlya Dryomov int ret; 887af3ea18SIlya Dryomov 893d14c5d2SYehuda Sadeh ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad); 903d14c5d2SYehuda Sadeh key->type = ceph_decode_16(p); 913d14c5d2SYehuda Sadeh ceph_decode_copy(p, &key->created, sizeof(key->created)); 923d14c5d2SYehuda Sadeh key->len = ceph_decode_16(p); 933d14c5d2SYehuda Sadeh ceph_decode_need(p, end, key->len, bad); 947af3ea18SIlya Dryomov ret = set_secret(key, *p); 957af3ea18SIlya Dryomov *p += key->len; 967af3ea18SIlya Dryomov return ret; 973d14c5d2SYehuda Sadeh 983d14c5d2SYehuda Sadeh bad: 993d14c5d2SYehuda Sadeh dout("failed to decode crypto key\n"); 1003d14c5d2SYehuda Sadeh return -EINVAL; 1013d14c5d2SYehuda Sadeh } 1023d14c5d2SYehuda Sadeh 1033d14c5d2SYehuda Sadeh int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey) 1043d14c5d2SYehuda Sadeh { 1053d14c5d2SYehuda Sadeh int inlen = strlen(inkey); 1063d14c5d2SYehuda Sadeh int blen = inlen * 3 / 4; 1073d14c5d2SYehuda Sadeh void *buf, *p; 1083d14c5d2SYehuda Sadeh int ret; 1093d14c5d2SYehuda Sadeh 1103d14c5d2SYehuda Sadeh dout("crypto_key_unarmor %s\n", inkey); 1113d14c5d2SYehuda Sadeh buf = kmalloc(blen, GFP_NOFS); 1123d14c5d2SYehuda Sadeh if (!buf) 1133d14c5d2SYehuda Sadeh return -ENOMEM; 1143d14c5d2SYehuda Sadeh blen = ceph_unarmor(buf, inkey, inkey+inlen); 1153d14c5d2SYehuda Sadeh if (blen < 0) { 1163d14c5d2SYehuda Sadeh kfree(buf); 1173d14c5d2SYehuda Sadeh return blen; 1183d14c5d2SYehuda Sadeh } 1193d14c5d2SYehuda Sadeh 1203d14c5d2SYehuda Sadeh p = buf; 1213d14c5d2SYehuda Sadeh ret = ceph_crypto_key_decode(key, &p, p + blen); 1223d14c5d2SYehuda Sadeh kfree(buf); 1233d14c5d2SYehuda Sadeh if (ret) 1243d14c5d2SYehuda Sadeh return ret; 1253d14c5d2SYehuda Sadeh dout("crypto_key_unarmor key %p type %d len %d\n", key, 1263d14c5d2SYehuda Sadeh key->type, key->len); 1273d14c5d2SYehuda Sadeh return 0; 1283d14c5d2SYehuda Sadeh } 1293d14c5d2SYehuda Sadeh 1306db2304aSIlya Dryomov void ceph_crypto_key_destroy(struct ceph_crypto_key *key) 1316db2304aSIlya Dryomov { 1326db2304aSIlya Dryomov if (key) { 1336db2304aSIlya Dryomov kfree(key->key); 1346db2304aSIlya Dryomov key->key = NULL; 1357af3ea18SIlya Dryomov crypto_free_skcipher(key->tfm); 1367af3ea18SIlya Dryomov key->tfm = NULL; 1376db2304aSIlya Dryomov } 1386db2304aSIlya Dryomov } 1396db2304aSIlya Dryomov 1403d14c5d2SYehuda Sadeh static const u8 *aes_iv = (u8 *)CEPH_AES_IV; 1413d14c5d2SYehuda Sadeh 142aaef3170SIlya Dryomov /* 143aaef3170SIlya Dryomov * Should be used for buffers allocated with ceph_kvmalloc(). 144aaef3170SIlya Dryomov * Currently these are encrypt out-buffer (ceph_buffer) and decrypt 145aaef3170SIlya Dryomov * in-buffer (msg front). 146aaef3170SIlya Dryomov * 147aaef3170SIlya Dryomov * Dispose of @sgt with teardown_sgtable(). 148aaef3170SIlya Dryomov * 149aaef3170SIlya Dryomov * @prealloc_sg is to avoid memory allocation inside sg_alloc_table() 150aaef3170SIlya Dryomov * in cases where a single sg is sufficient. No attempt to reduce the 151aaef3170SIlya Dryomov * number of sgs by squeezing physically contiguous pages together is 152aaef3170SIlya Dryomov * made though, for simplicity. 153aaef3170SIlya Dryomov */ 154aaef3170SIlya Dryomov static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg, 155aaef3170SIlya Dryomov const void *buf, unsigned int buf_len) 156aaef3170SIlya Dryomov { 157aaef3170SIlya Dryomov struct scatterlist *sg; 158aaef3170SIlya Dryomov const bool is_vmalloc = is_vmalloc_addr(buf); 159aaef3170SIlya Dryomov unsigned int off = offset_in_page(buf); 160aaef3170SIlya Dryomov unsigned int chunk_cnt = 1; 161aaef3170SIlya Dryomov unsigned int chunk_len = PAGE_ALIGN(off + buf_len); 162aaef3170SIlya Dryomov int i; 163aaef3170SIlya Dryomov int ret; 164aaef3170SIlya Dryomov 165aaef3170SIlya Dryomov if (buf_len == 0) { 166aaef3170SIlya Dryomov memset(sgt, 0, sizeof(*sgt)); 167aaef3170SIlya Dryomov return -EINVAL; 168aaef3170SIlya Dryomov } 169aaef3170SIlya Dryomov 170aaef3170SIlya Dryomov if (is_vmalloc) { 171aaef3170SIlya Dryomov chunk_cnt = chunk_len >> PAGE_SHIFT; 172aaef3170SIlya Dryomov chunk_len = PAGE_SIZE; 173aaef3170SIlya Dryomov } 174aaef3170SIlya Dryomov 175aaef3170SIlya Dryomov if (chunk_cnt > 1) { 176aaef3170SIlya Dryomov ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS); 177aaef3170SIlya Dryomov if (ret) 178aaef3170SIlya Dryomov return ret; 179aaef3170SIlya Dryomov } else { 180aaef3170SIlya Dryomov WARN_ON(chunk_cnt != 1); 181aaef3170SIlya Dryomov sg_init_table(prealloc_sg, 1); 182aaef3170SIlya Dryomov sgt->sgl = prealloc_sg; 183aaef3170SIlya Dryomov sgt->nents = sgt->orig_nents = 1; 184aaef3170SIlya Dryomov } 185aaef3170SIlya Dryomov 186aaef3170SIlya Dryomov for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { 187aaef3170SIlya Dryomov struct page *page; 188aaef3170SIlya Dryomov unsigned int len = min(chunk_len - off, buf_len); 189aaef3170SIlya Dryomov 190aaef3170SIlya Dryomov if (is_vmalloc) 191aaef3170SIlya Dryomov page = vmalloc_to_page(buf); 192aaef3170SIlya Dryomov else 193aaef3170SIlya Dryomov page = virt_to_page(buf); 194aaef3170SIlya Dryomov 195aaef3170SIlya Dryomov sg_set_page(sg, page, len, off); 196aaef3170SIlya Dryomov 197aaef3170SIlya Dryomov off = 0; 198aaef3170SIlya Dryomov buf += len; 199aaef3170SIlya Dryomov buf_len -= len; 200aaef3170SIlya Dryomov } 201aaef3170SIlya Dryomov WARN_ON(buf_len != 0); 202aaef3170SIlya Dryomov 203aaef3170SIlya Dryomov return 0; 204aaef3170SIlya Dryomov } 205aaef3170SIlya Dryomov 206aaef3170SIlya Dryomov static void teardown_sgtable(struct sg_table *sgt) 207aaef3170SIlya Dryomov { 208aaef3170SIlya Dryomov if (sgt->orig_nents > 1) 209aaef3170SIlya Dryomov sg_free_table(sgt); 210aaef3170SIlya Dryomov } 211aaef3170SIlya Dryomov 212a45f795cSIlya Dryomov static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt, 213a45f795cSIlya Dryomov void *buf, int buf_len, int in_len, int *pout_len) 214a45f795cSIlya Dryomov { 2157af3ea18SIlya Dryomov SKCIPHER_REQUEST_ON_STACK(req, key->tfm); 216a45f795cSIlya Dryomov struct sg_table sgt; 217a45f795cSIlya Dryomov struct scatterlist prealloc_sg; 218124f930bSIlya Dryomov char iv[AES_BLOCK_SIZE] __aligned(8); 219a45f795cSIlya Dryomov int pad_byte = AES_BLOCK_SIZE - (in_len & (AES_BLOCK_SIZE - 1)); 220a45f795cSIlya Dryomov int crypt_len = encrypt ? in_len + pad_byte : in_len; 221a45f795cSIlya Dryomov int ret; 222a45f795cSIlya Dryomov 223a45f795cSIlya Dryomov WARN_ON(crypt_len > buf_len); 224a45f795cSIlya Dryomov if (encrypt) 225a45f795cSIlya Dryomov memset(buf + in_len, pad_byte, pad_byte); 226a45f795cSIlya Dryomov ret = setup_sgtable(&sgt, &prealloc_sg, buf, crypt_len); 227a45f795cSIlya Dryomov if (ret) 2287af3ea18SIlya Dryomov return ret; 229a45f795cSIlya Dryomov 230a45f795cSIlya Dryomov memcpy(iv, aes_iv, AES_BLOCK_SIZE); 2317af3ea18SIlya Dryomov skcipher_request_set_tfm(req, key->tfm); 232a45f795cSIlya Dryomov skcipher_request_set_callback(req, 0, NULL, NULL); 233a45f795cSIlya Dryomov skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv); 234a45f795cSIlya Dryomov 235a45f795cSIlya Dryomov /* 236a45f795cSIlya Dryomov print_hex_dump(KERN_ERR, "key: ", DUMP_PREFIX_NONE, 16, 1, 237a45f795cSIlya Dryomov key->key, key->len, 1); 238a45f795cSIlya Dryomov print_hex_dump(KERN_ERR, " in: ", DUMP_PREFIX_NONE, 16, 1, 239a45f795cSIlya Dryomov buf, crypt_len, 1); 240a45f795cSIlya Dryomov */ 241a45f795cSIlya Dryomov if (encrypt) 242a45f795cSIlya Dryomov ret = crypto_skcipher_encrypt(req); 243a45f795cSIlya Dryomov else 244a45f795cSIlya Dryomov ret = crypto_skcipher_decrypt(req); 245a45f795cSIlya Dryomov skcipher_request_zero(req); 246a45f795cSIlya Dryomov if (ret) { 247a45f795cSIlya Dryomov pr_err("%s %scrypt failed: %d\n", __func__, 248a45f795cSIlya Dryomov encrypt ? "en" : "de", ret); 249a45f795cSIlya Dryomov goto out_sgt; 250a45f795cSIlya Dryomov } 251a45f795cSIlya Dryomov /* 252a45f795cSIlya Dryomov print_hex_dump(KERN_ERR, "out: ", DUMP_PREFIX_NONE, 16, 1, 253a45f795cSIlya Dryomov buf, crypt_len, 1); 254a45f795cSIlya Dryomov */ 255a45f795cSIlya Dryomov 256a45f795cSIlya Dryomov if (encrypt) { 257a45f795cSIlya Dryomov *pout_len = crypt_len; 258a45f795cSIlya Dryomov } else { 259a45f795cSIlya Dryomov pad_byte = *(char *)(buf + in_len - 1); 260a45f795cSIlya Dryomov if (pad_byte > 0 && pad_byte <= AES_BLOCK_SIZE && 261a45f795cSIlya Dryomov in_len >= pad_byte) { 262a45f795cSIlya Dryomov *pout_len = in_len - pad_byte; 263a45f795cSIlya Dryomov } else { 264a45f795cSIlya Dryomov pr_err("%s got bad padding %d on in_len %d\n", 265a45f795cSIlya Dryomov __func__, pad_byte, in_len); 266a45f795cSIlya Dryomov ret = -EPERM; 267a45f795cSIlya Dryomov goto out_sgt; 268a45f795cSIlya Dryomov } 269a45f795cSIlya Dryomov } 270a45f795cSIlya Dryomov 271a45f795cSIlya Dryomov out_sgt: 272a45f795cSIlya Dryomov teardown_sgtable(&sgt); 273a45f795cSIlya Dryomov return ret; 274a45f795cSIlya Dryomov } 275a45f795cSIlya Dryomov 276a45f795cSIlya Dryomov int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt, 277a45f795cSIlya Dryomov void *buf, int buf_len, int in_len, int *pout_len) 278a45f795cSIlya Dryomov { 279a45f795cSIlya Dryomov switch (key->type) { 280a45f795cSIlya Dryomov case CEPH_CRYPTO_NONE: 281a45f795cSIlya Dryomov *pout_len = in_len; 282a45f795cSIlya Dryomov return 0; 283a45f795cSIlya Dryomov case CEPH_CRYPTO_AES: 284a45f795cSIlya Dryomov return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len, 285a45f795cSIlya Dryomov pout_len); 286a45f795cSIlya Dryomov default: 287a45f795cSIlya Dryomov return -ENOTSUPP; 288a45f795cSIlya Dryomov } 289a45f795cSIlya Dryomov } 290a45f795cSIlya Dryomov 291efa64c09SDavid Howells static int ceph_key_preparse(struct key_preparsed_payload *prep) 2924b2a58abSTommi Virtanen { 2934b2a58abSTommi Virtanen struct ceph_crypto_key *ckey; 294cf7f601cSDavid Howells size_t datalen = prep->datalen; 2954b2a58abSTommi Virtanen int ret; 2964b2a58abSTommi Virtanen void *p; 2974b2a58abSTommi Virtanen 2984b2a58abSTommi Virtanen ret = -EINVAL; 299cf7f601cSDavid Howells if (datalen <= 0 || datalen > 32767 || !prep->data) 3004b2a58abSTommi Virtanen goto err; 3014b2a58abSTommi Virtanen 3024b2a58abSTommi Virtanen ret = -ENOMEM; 3034b2a58abSTommi Virtanen ckey = kmalloc(sizeof(*ckey), GFP_KERNEL); 3044b2a58abSTommi Virtanen if (!ckey) 3054b2a58abSTommi Virtanen goto err; 3064b2a58abSTommi Virtanen 3074b2a58abSTommi Virtanen /* TODO ceph_crypto_key_decode should really take const input */ 308cf7f601cSDavid Howells p = (void *)prep->data; 309cf7f601cSDavid Howells ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen); 3104b2a58abSTommi Virtanen if (ret < 0) 3114b2a58abSTommi Virtanen goto err_ckey; 3124b2a58abSTommi Virtanen 313146aa8b1SDavid Howells prep->payload.data[0] = ckey; 314efa64c09SDavid Howells prep->quotalen = datalen; 3154b2a58abSTommi Virtanen return 0; 3164b2a58abSTommi Virtanen 3174b2a58abSTommi Virtanen err_ckey: 3184b2a58abSTommi Virtanen kfree(ckey); 3194b2a58abSTommi Virtanen err: 3204b2a58abSTommi Virtanen return ret; 3214b2a58abSTommi Virtanen } 3224b2a58abSTommi Virtanen 323efa64c09SDavid Howells static void ceph_key_free_preparse(struct key_preparsed_payload *prep) 324efa64c09SDavid Howells { 325146aa8b1SDavid Howells struct ceph_crypto_key *ckey = prep->payload.data[0]; 326efa64c09SDavid Howells ceph_crypto_key_destroy(ckey); 327efa64c09SDavid Howells kfree(ckey); 328efa64c09SDavid Howells } 329efa64c09SDavid Howells 330efa64c09SDavid Howells static void ceph_key_destroy(struct key *key) 331efa64c09SDavid Howells { 332146aa8b1SDavid Howells struct ceph_crypto_key *ckey = key->payload.data[0]; 3334b2a58abSTommi Virtanen 3344b2a58abSTommi Virtanen ceph_crypto_key_destroy(ckey); 335f0666b1aSSylvain Munaut kfree(ckey); 3364b2a58abSTommi Virtanen } 3374b2a58abSTommi Virtanen 3384b2a58abSTommi Virtanen struct key_type key_type_ceph = { 3394b2a58abSTommi Virtanen .name = "ceph", 340efa64c09SDavid Howells .preparse = ceph_key_preparse, 341efa64c09SDavid Howells .free_preparse = ceph_key_free_preparse, 342efa64c09SDavid Howells .instantiate = generic_key_instantiate, 3434b2a58abSTommi Virtanen .destroy = ceph_key_destroy, 3444b2a58abSTommi Virtanen }; 3454b2a58abSTommi Virtanen 3464b2a58abSTommi Virtanen int ceph_crypto_init(void) { 3474b2a58abSTommi Virtanen return register_key_type(&key_type_ceph); 3484b2a58abSTommi Virtanen } 3494b2a58abSTommi Virtanen 3504b2a58abSTommi Virtanen void ceph_crypto_shutdown(void) { 3514b2a58abSTommi Virtanen unregister_key_type(&key_type_ceph); 3524b2a58abSTommi Virtanen } 353