Whamcloud - gitweb
LU-17705 ptlrpc: replace synchronize_rcu() with rcu_barrier()
[fs/lustre-release.git] / lustre / ptlrpc / gss / gss_crypto.c
1 /*
2  * Modifications for Lustre
3  *
4  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
5  *
6  * Author: Eric Mei <ericm@clusterfs.com>
7  */
8
9 /*
10  *  linux/net/sunrpc/gss_krb5_mech.c
11  *  linux/net/sunrpc/gss_krb5_crypto.c
12  *  linux/net/sunrpc/gss_krb5_seal.c
13  *  linux/net/sunrpc/gss_krb5_seqnum.c
14  *  linux/net/sunrpc/gss_krb5_unseal.c
15  *
16  *  Copyright (c) 2001 The Regents of the University of Michigan.
17  *  All rights reserved.
18  *
19  *  Andy Adamson <andros@umich.edu>
20  *  J. Bruce Fields <bfields@umich.edu>
21  *
22  *  Redistribution and use in source and binary forms, with or without
23  *  modification, are permitted provided that the following conditions
24  *  are met:
25  *
26  *  1. Redistributions of source code must retain the above copyright
27  *     notice, this list of conditions and the following disclaimer.
28  *  2. Redistributions in binary form must reproduce the above copyright
29  *     notice, this list of conditions and the following disclaimer in the
30  *     documentation and/or other materials provided with the distribution.
31  *  3. Neither the name of the University nor the names of its
32  *     contributors may be used to endorse or promote products derived
33  *     from this software without specific prior written permission.
34  *
35  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
36  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
37  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
43  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
44  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
45  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46  *
47  */
48
49 #define DEBUG_SUBSYSTEM S_SEC
50
51 #include <obd.h>
52 #include <obd_support.h>
53
54 #include "gss_internal.h"
55 #include "gss_crypto.h"
56
57 int gss_keyblock_init(struct gss_keyblock *kb, const char *alg_name,
58                       const int alg_mode)
59 {
60         int rc;
61
62         kb->kb_tfm = crypto_alloc_sync_skcipher(alg_name, alg_mode, 0);
63         if (IS_ERR(kb->kb_tfm)) {
64                 rc = PTR_ERR(kb->kb_tfm);
65                 kb->kb_tfm = NULL;
66                 CERROR("failed to alloc tfm: %s, mode %d: rc = %d\n", alg_name,
67                        alg_mode, rc);
68                 return rc;
69         }
70
71         rc = crypto_sync_skcipher_setkey(kb->kb_tfm, kb->kb_key.data,
72                                          kb->kb_key.len);
73         if (rc) {
74                 CERROR("failed to set %s key, len %d, rc = %d\n", alg_name,
75                        kb->kb_key.len, rc);
76                 return rc;
77         }
78
79         return 0;
80 }
81
82 void gss_keyblock_free(struct gss_keyblock *kb)
83 {
84         rawobj_free(&kb->kb_key);
85         if (kb->kb_tfm)
86                 crypto_free_sync_skcipher(kb->kb_tfm);
87 }
88
89 int gss_keyblock_dup(struct gss_keyblock *new, struct gss_keyblock *kb)
90 {
91         return rawobj_dup(&new->kb_key, &kb->kb_key);
92 }
93
94 int gss_get_bytes(char **ptr, const char *end, void *res, size_t len)
95 {
96         char *p, *q;
97         p = *ptr;
98         q = p + len;
99         if (q > end || q < p)
100                 return -EINVAL;
101         memcpy(res, p, len);
102         *ptr = q;
103         return 0;
104 }
105
106 int gss_get_rawobj(char **ptr, const char *end, rawobj_t *res)
107 {
108         char   *p, *q;
109         __u32   len;
110
111         p = *ptr;
112         if (gss_get_bytes(&p, end, &len, sizeof(len)))
113                 return -EINVAL;
114
115         q = p + len;
116         if (q > end || q < p)
117                 return -EINVAL;
118
119         /* Support empty objects */
120         if (len != 0) {
121                 OBD_ALLOC_LARGE(res->data, len);
122                 if (!res->data)
123                         return -ENOMEM;
124         } else {
125                 res->len = len;
126                 res->data = NULL;
127                 return 0;
128         }
129
130         res->len = len;
131         memcpy(res->data, p, len);
132         *ptr = q;
133         return 0;
134 }
135
136 int gss_get_keyblock(char **ptr, const char *end,
137                      struct gss_keyblock *kb, __u32 keysize)
138 {
139         char *buf;
140         int rc;
141
142         OBD_ALLOC_LARGE(buf, keysize);
143         if (buf == NULL)
144                 return -ENOMEM;
145
146         rc = gss_get_bytes(ptr, end, buf, keysize);
147         if (rc) {
148                 OBD_FREE_LARGE(buf, keysize);
149                 return rc;
150         }
151
152         kb->kb_key.len = keysize;
153         kb->kb_key.data = buf;
154         return 0;
155 }
156
157 /*
158  * Should be used for buffers allocated with k/vmalloc().
159  *
160  * Dispose of @sgt with gss_teardown_sgtable().
161  *
162  * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
163  * in cases where a single sg is sufficient.  No attempt to reduce the
164  * number of sgs by squeezing physically contiguous pages together is
165  * made though, for simplicity.
166  *
167  * This function is copied from the ceph filesystem code.
168  */
169 int gss_setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
170                       const void *buf, unsigned int buf_len)
171 {
172         struct scatterlist *sg;
173         const bool is_vmalloc = is_vmalloc_addr(buf);
174         unsigned int off = offset_in_page(buf);
175         unsigned int chunk_cnt = 1;
176         unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
177         int i;
178         int rc;
179
180         if (buf_len == 0) {
181                 memset(sgt, 0, sizeof(*sgt));
182                 return -EINVAL;
183         }
184
185         if (is_vmalloc) {
186                 chunk_cnt = chunk_len >> PAGE_SHIFT;
187                 chunk_len = PAGE_SIZE;
188         }
189
190         if (chunk_cnt > 1) {
191                 rc = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
192                 if (rc)
193                         return rc;
194         } else {
195                 WARN_ON_ONCE(chunk_cnt != 1);
196                 sg_init_table(prealloc_sg, 1);
197                 sgt->sgl = prealloc_sg;
198                 sgt->nents = sgt->orig_nents = 1;
199         }
200
201         for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
202                 struct page *page;
203                 unsigned int len = min(chunk_len - off, buf_len);
204
205                 if (is_vmalloc)
206                         page = vmalloc_to_page(buf);
207                 else
208                         page = virt_to_page(buf);
209
210                 sg_set_page(sg, page, len, off);
211
212                 off = 0;
213                 buf += len;
214                 buf_len -= len;
215         }
216
217         WARN_ON_ONCE(buf_len != 0);
218
219         return 0;
220 }
221
222 void gss_teardown_sgtable(struct sg_table *sgt)
223 {
224         if (sgt->orig_nents > 1)
225                 sg_free_table(sgt);
226 }
227
228 int gss_crypt_generic(struct crypto_sync_skcipher *tfm, int decrypt,
229                       const void *iv, const void *in, void *out, size_t length)
230 {
231         struct scatterlist sg;
232         struct sg_table sg_out;
233         __u8 local_iv[16] = {0};
234         __u32 ret = -EINVAL;
235         SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
236
237         LASSERT(tfm);
238
239         if (length % crypto_sync_skcipher_blocksize(tfm) != 0) {
240                 CERROR("output length %zu mismatch blocksize %d\n",
241                        length, crypto_sync_skcipher_blocksize(tfm));
242                 goto out;
243         }
244
245         if (crypto_sync_skcipher_ivsize(tfm) > ARRAY_SIZE(local_iv)) {
246                 CERROR("iv size too large %d\n",
247                         crypto_sync_skcipher_ivsize(tfm));
248                 goto out;
249         }
250
251         if (iv)
252                 memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
253
254         if (in != out)
255                 memmove(out, in, length);
256
257         ret = gss_setup_sgtable(&sg_out, &sg, out, length);
258         if (ret != 0)
259                 goto out;
260
261         skcipher_request_set_sync_tfm(req, tfm);
262         skcipher_request_set_callback(req, 0, NULL, NULL);
263         skcipher_request_set_crypt(req, &sg, &sg, length, local_iv);
264
265         if (decrypt)
266                 ret = crypto_skcipher_decrypt_iv(req, &sg, &sg, length);
267         else
268                 ret = crypto_skcipher_encrypt_iv(req, &sg, &sg, length);
269
270         skcipher_request_zero(req);
271         gss_teardown_sgtable(&sg_out);
272 out:
273         return ret;
274 }
275
276 int gss_digest_hash(struct ahash_request *req,
277                     rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
278                     int iovcnt, struct bio_vec *iovs)
279 {
280         struct scatterlist sg[1];
281         struct sg_table sgt;
282         int rc = 0;
283         int i;
284
285         for (i = 0; i < msgcnt; i++) {
286                 if (msgs[i].len == 0)
287                         continue;
288
289                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
290                 if (rc)
291                         return rc;
292
293                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
294                 rc = crypto_ahash_update(req);
295                 gss_teardown_sgtable(&sgt);
296                 if (rc)
297                         return rc;
298         }
299
300         for (i = 0; i < iovcnt; i++) {
301                 if (iovs[i].bv_len == 0)
302                         continue;
303
304                 sg_init_table(sg, 1);
305                 sg_set_page(&sg[0], iovs[i].bv_page, iovs[i].bv_len,
306                             iovs[i].bv_offset);
307
308                 ahash_request_set_crypt(req, sg, NULL, iovs[i].bv_len);
309                 rc = crypto_ahash_update(req);
310                 if (rc)
311                         return rc;
312         }
313
314         if (hdr) {
315                 rc = gss_setup_sgtable(&sgt, sg, hdr->data, hdr->len);
316                 if (rc)
317                         return rc;
318
319                 ahash_request_set_crypt(req, sg, NULL, hdr->len);
320                 rc = crypto_ahash_update(req);
321                 gss_teardown_sgtable(&sgt);
322                 if (rc)
323                         return rc;
324         }
325
326         return rc;
327 }
328
329 int gss_digest_hash_compat(struct ahash_request *req,
330                            rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
331                            int iovcnt, struct bio_vec *iovs)
332 {
333         struct scatterlist sg[1];
334         struct sg_table sgt;
335         int rc = 0;
336         int i;
337
338         for (i = 0; i < msgcnt; i++) {
339                 if (msgs[i].len == 0)
340                         continue;
341
342                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
343                 if (rc)
344                         return rc;
345
346                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
347                 rc = crypto_ahash_update(req);
348                 gss_teardown_sgtable(&sgt);
349                 if (rc)
350                         return rc;
351         }
352
353         for (i = 0; i < iovcnt; i++) {
354                 if (iovs[i].bv_len == 0)
355                         continue;
356
357                 sg_init_table(sg, 1);
358                 sg_set_page(&sg[0], iovs[i].bv_page, iovs[i].bv_len,
359                             iovs[i].bv_offset);
360
361                 ahash_request_set_crypt(req, sg, NULL, iovs[i].bv_len);
362                 rc = crypto_ahash_update(req);
363                 if (rc)
364                         return rc;
365         }
366
367         if (hdr) {
368                 rc = gss_setup_sgtable(&sgt, sg, &(hdr->len), sizeof(hdr->len));
369                 if (rc)
370                         return rc;
371
372                 ahash_request_set_crypt(req, sg, NULL, sizeof(hdr->len));
373                 rc = crypto_ahash_update(req);
374                 gss_teardown_sgtable(&sgt);
375                 if (rc)
376                         return rc;
377         }
378
379         return rc;
380 }
381
382 int gss_add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
383 {
384         int padding;
385
386         padding = (blocksize - (msg->len & (blocksize - 1))) &
387                   (blocksize - 1);
388         if (!padding)
389                 return 0;
390
391         if (msg->len + padding > msg_buflen) {
392                 CERROR("bufsize %u too small: datalen %u, padding %u\n",
393                        msg_buflen, msg->len, padding);
394                 return -EINVAL;
395         }
396
397         memset(msg->data + msg->len, padding, padding);
398         msg->len += padding;
399         return 0;
400 }
401
402 int gss_crypt_rawobjs(struct crypto_sync_skcipher *tfm, __u8 *iv,
403                       int inobj_cnt, rawobj_t *inobjs, rawobj_t *outobj,
404                       int enc)
405 {
406         struct scatterlist src;
407         struct scatterlist dst;
408         struct sg_table sg_dst;
409         struct sg_table sg_src;
410         __u8 *buf;
411         __u32 datalen = 0;
412         int i, rc;
413         SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
414
415         ENTRY;
416
417         buf = outobj->data;
418         skcipher_request_set_sync_tfm(req, tfm);
419         skcipher_request_set_callback(req, 0, NULL, NULL);
420
421         for (i = 0; i < inobj_cnt; i++) {
422                 LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
423
424                 rc = gss_setup_sgtable(&sg_src, &src, inobjs[i].data,
425                                    inobjs[i].len);
426                 if (rc != 0)
427                         RETURN(rc);
428
429                 rc = gss_setup_sgtable(&sg_dst, &dst, buf,
430                                        outobj->len - datalen);
431                 if (rc != 0) {
432                         gss_teardown_sgtable(&sg_src);
433                         RETURN(rc);
434                 }
435
436                 skcipher_request_set_crypt(req, &src, &dst, src.length, iv);
437                 if (!iv)
438                         skcipher_request_set_crypt_iv(req);
439
440                 if (enc)
441                         rc = crypto_skcipher_encrypt_iv(req, &dst, &src,
442                                                         src.length);
443                 else
444                         rc = crypto_skcipher_decrypt_iv(req, &dst, &src,
445                                                         src.length);
446
447                 gss_teardown_sgtable(&sg_src);
448                 gss_teardown_sgtable(&sg_dst);
449
450                 if (rc) {
451                         CERROR("encrypt error %d\n", rc);
452                         skcipher_request_zero(req);
453                         RETURN(rc);
454                 }
455
456                 datalen += inobjs[i].len;
457                 buf += inobjs[i].len;
458         }
459         skcipher_request_zero(req);
460
461         outobj->len = datalen;
462         RETURN(0);
463 }