Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ceph/crypto.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
#include <linux/ceph/ceph_debug.h>
4
5
#include <linux/err.h>
6
#include <linux/scatterlist.h>
7
#include <linux/sched.h>
8
#include <linux/slab.h>
9
#include <crypto/aes.h>
10
#include <crypto/skcipher.h>
11
#include <linux/key-type.h>
12
#include <linux/sched/mm.h>
13
14
#include <keys/ceph-type.h>
15
#include <keys/user-type.h>
16
#include <linux/ceph/decode.h>
17
#include "crypto.h"
18
19
/*
20
* Set ->key and ->tfm. The rest of the key should be filled in before
21
* this function is called.
22
*/
23
static int set_secret(struct ceph_crypto_key *key, void *buf)
24
{
25
unsigned int noio_flag;
26
int ret;
27
28
key->key = NULL;
29
key->tfm = NULL;
30
31
switch (key->type) {
32
case CEPH_CRYPTO_NONE:
33
return 0; /* nothing to do */
34
case CEPH_CRYPTO_AES:
35
break;
36
default:
37
return -ENOTSUPP;
38
}
39
40
if (!key->len)
41
return -EINVAL;
42
43
key->key = kmemdup(buf, key->len, GFP_NOIO);
44
if (!key->key) {
45
ret = -ENOMEM;
46
goto fail;
47
}
48
49
/* crypto_alloc_sync_skcipher() allocates with GFP_KERNEL */
50
noio_flag = memalloc_noio_save();
51
key->tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
52
memalloc_noio_restore(noio_flag);
53
if (IS_ERR(key->tfm)) {
54
ret = PTR_ERR(key->tfm);
55
key->tfm = NULL;
56
goto fail;
57
}
58
59
ret = crypto_sync_skcipher_setkey(key->tfm, key->key, key->len);
60
if (ret)
61
goto fail;
62
63
return 0;
64
65
fail:
66
ceph_crypto_key_destroy(key);
67
return ret;
68
}
69
70
int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
71
const struct ceph_crypto_key *src)
72
{
73
memcpy(dst, src, sizeof(struct ceph_crypto_key));
74
return set_secret(dst, src->key);
75
}
76
77
int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end)
78
{
79
int ret;
80
81
ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad);
82
key->type = ceph_decode_16(p);
83
ceph_decode_copy(p, &key->created, sizeof(key->created));
84
key->len = ceph_decode_16(p);
85
ceph_decode_need(p, end, key->len, bad);
86
ret = set_secret(key, *p);
87
memzero_explicit(*p, key->len);
88
*p += key->len;
89
return ret;
90
91
bad:
92
dout("failed to decode crypto key\n");
93
return -EINVAL;
94
}
95
96
int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey)
97
{
98
int inlen = strlen(inkey);
99
int blen = inlen * 3 / 4;
100
void *buf, *p;
101
int ret;
102
103
dout("crypto_key_unarmor %s\n", inkey);
104
buf = kmalloc(blen, GFP_NOFS);
105
if (!buf)
106
return -ENOMEM;
107
blen = ceph_unarmor(buf, inkey, inkey+inlen);
108
if (blen < 0) {
109
kfree(buf);
110
return blen;
111
}
112
113
p = buf;
114
ret = ceph_crypto_key_decode(key, &p, p + blen);
115
kfree(buf);
116
if (ret)
117
return ret;
118
dout("crypto_key_unarmor key %p type %d len %d\n", key,
119
key->type, key->len);
120
return 0;
121
}
122
123
void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
124
{
125
if (key) {
126
kfree_sensitive(key->key);
127
key->key = NULL;
128
if (key->tfm) {
129
crypto_free_sync_skcipher(key->tfm);
130
key->tfm = NULL;
131
}
132
}
133
}
134
135
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
136
137
/*
138
* Should be used for buffers allocated with kvmalloc().
139
* Currently these are encrypt out-buffer (ceph_buffer) and decrypt
140
* in-buffer (msg front).
141
*
142
* Dispose of @sgt with teardown_sgtable().
143
*
144
* @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
145
* in cases where a single sg is sufficient. No attempt to reduce the
146
* number of sgs by squeezing physically contiguous pages together is
147
* made though, for simplicity.
148
*/
149
static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
150
const void *buf, unsigned int buf_len)
151
{
152
struct scatterlist *sg;
153
const bool is_vmalloc = is_vmalloc_addr(buf);
154
unsigned int off = offset_in_page(buf);
155
unsigned int chunk_cnt = 1;
156
unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
157
int i;
158
int ret;
159
160
if (buf_len == 0) {
161
memset(sgt, 0, sizeof(*sgt));
162
return -EINVAL;
163
}
164
165
if (is_vmalloc) {
166
chunk_cnt = chunk_len >> PAGE_SHIFT;
167
chunk_len = PAGE_SIZE;
168
}
169
170
if (chunk_cnt > 1) {
171
ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
172
if (ret)
173
return ret;
174
} else {
175
WARN_ON(chunk_cnt != 1);
176
sg_init_table(prealloc_sg, 1);
177
sgt->sgl = prealloc_sg;
178
sgt->nents = sgt->orig_nents = 1;
179
}
180
181
for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
182
struct page *page;
183
unsigned int len = min(chunk_len - off, buf_len);
184
185
if (is_vmalloc)
186
page = vmalloc_to_page(buf);
187
else
188
page = virt_to_page(buf);
189
190
sg_set_page(sg, page, len, off);
191
192
off = 0;
193
buf += len;
194
buf_len -= len;
195
}
196
WARN_ON(buf_len != 0);
197
198
return 0;
199
}
200
201
static void teardown_sgtable(struct sg_table *sgt)
202
{
203
if (sgt->orig_nents > 1)
204
sg_free_table(sgt);
205
}
206
207
static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt,
208
void *buf, int buf_len, int in_len, int *pout_len)
209
{
210
SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm);
211
struct sg_table sgt;
212
struct scatterlist prealloc_sg;
213
char iv[AES_BLOCK_SIZE] __aligned(8);
214
int pad_byte = AES_BLOCK_SIZE - (in_len & (AES_BLOCK_SIZE - 1));
215
int crypt_len = encrypt ? in_len + pad_byte : in_len;
216
int ret;
217
218
WARN_ON(crypt_len > buf_len);
219
if (encrypt)
220
memset(buf + in_len, pad_byte, pad_byte);
221
ret = setup_sgtable(&sgt, &prealloc_sg, buf, crypt_len);
222
if (ret)
223
return ret;
224
225
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
226
skcipher_request_set_sync_tfm(req, key->tfm);
227
skcipher_request_set_callback(req, 0, NULL, NULL);
228
skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv);
229
230
/*
231
print_hex_dump(KERN_ERR, "key: ", DUMP_PREFIX_NONE, 16, 1,
232
key->key, key->len, 1);
233
print_hex_dump(KERN_ERR, " in: ", DUMP_PREFIX_NONE, 16, 1,
234
buf, crypt_len, 1);
235
*/
236
if (encrypt)
237
ret = crypto_skcipher_encrypt(req);
238
else
239
ret = crypto_skcipher_decrypt(req);
240
skcipher_request_zero(req);
241
if (ret) {
242
pr_err("%s %scrypt failed: %d\n", __func__,
243
encrypt ? "en" : "de", ret);
244
goto out_sgt;
245
}
246
/*
247
print_hex_dump(KERN_ERR, "out: ", DUMP_PREFIX_NONE, 16, 1,
248
buf, crypt_len, 1);
249
*/
250
251
if (encrypt) {
252
*pout_len = crypt_len;
253
} else {
254
pad_byte = *(char *)(buf + in_len - 1);
255
if (pad_byte > 0 && pad_byte <= AES_BLOCK_SIZE &&
256
in_len >= pad_byte) {
257
*pout_len = in_len - pad_byte;
258
} else {
259
pr_err("%s got bad padding %d on in_len %d\n",
260
__func__, pad_byte, in_len);
261
ret = -EPERM;
262
goto out_sgt;
263
}
264
}
265
266
out_sgt:
267
teardown_sgtable(&sgt);
268
return ret;
269
}
270
271
int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
272
void *buf, int buf_len, int in_len, int *pout_len)
273
{
274
switch (key->type) {
275
case CEPH_CRYPTO_NONE:
276
*pout_len = in_len;
277
return 0;
278
case CEPH_CRYPTO_AES:
279
return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len,
280
pout_len);
281
default:
282
return -ENOTSUPP;
283
}
284
}
285
286
static int ceph_key_preparse(struct key_preparsed_payload *prep)
287
{
288
struct ceph_crypto_key *ckey;
289
size_t datalen = prep->datalen;
290
int ret;
291
void *p;
292
293
ret = -EINVAL;
294
if (datalen <= 0 || datalen > 32767 || !prep->data)
295
goto err;
296
297
ret = -ENOMEM;
298
ckey = kmalloc(sizeof(*ckey), GFP_KERNEL);
299
if (!ckey)
300
goto err;
301
302
/* TODO ceph_crypto_key_decode should really take const input */
303
p = (void *)prep->data;
304
ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen);
305
if (ret < 0)
306
goto err_ckey;
307
308
prep->payload.data[0] = ckey;
309
prep->quotalen = datalen;
310
return 0;
311
312
err_ckey:
313
kfree(ckey);
314
err:
315
return ret;
316
}
317
318
static void ceph_key_free_preparse(struct key_preparsed_payload *prep)
319
{
320
struct ceph_crypto_key *ckey = prep->payload.data[0];
321
ceph_crypto_key_destroy(ckey);
322
kfree(ckey);
323
}
324
325
static void ceph_key_destroy(struct key *key)
326
{
327
struct ceph_crypto_key *ckey = key->payload.data[0];
328
329
ceph_crypto_key_destroy(ckey);
330
kfree(ckey);
331
}
332
333
struct key_type key_type_ceph = {
334
.name = "ceph",
335
.preparse = ceph_key_preparse,
336
.free_preparse = ceph_key_free_preparse,
337
.instantiate = generic_key_instantiate,
338
.destroy = ceph_key_destroy,
339
};
340
341
int __init ceph_crypto_init(void)
342
{
343
return register_key_type(&key_type_ceph);
344
}
345
346
void ceph_crypto_shutdown(void)
347
{
348
unregister_key_type(&key_type_ceph);
349
}
350
351