Whamcloud - gitweb
LU-13344 gss: Update crypto to use sync_skcipher
[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 <libcfs/linux/linux-crypto.h>
52 #include <obd.h>
53 #include <obd_support.h>
54
55 #include "gss_internal.h"
56 #include "gss_crypto.h"
57
58 int gss_keyblock_init(struct gss_keyblock *kb, const char *alg_name,
59                       const int alg_mode)
60 {
61         int rc;
62
63         kb->kb_tfm = crypto_alloc_sync_skcipher(alg_name, alg_mode, 0);
64         if (IS_ERR(kb->kb_tfm)) {
65                 rc = PTR_ERR(kb->kb_tfm);
66                 kb->kb_tfm = NULL;
67                 CERROR("failed to alloc tfm: %s, mode %d: rc = %d\n", alg_name,
68                        alg_mode, rc);
69                 return rc;
70         }
71
72         rc = crypto_sync_skcipher_setkey(kb->kb_tfm, kb->kb_key.data,
73                                          kb->kb_key.len);
74         if (rc) {
75                 CERROR("failed to set %s key, len %d, rc = %d\n", alg_name,
76                        kb->kb_key.len, rc);
77                 return rc;
78         }
79
80         return 0;
81 }
82
83 void gss_keyblock_free(struct gss_keyblock *kb)
84 {
85         rawobj_free(&kb->kb_key);
86         if (kb->kb_tfm)
87                 crypto_free_sync_skcipher(kb->kb_tfm);
88 }
89
90 int gss_keyblock_dup(struct gss_keyblock *new, struct gss_keyblock *kb)
91 {
92         return rawobj_dup(&new->kb_key, &kb->kb_key);
93 }
94
95 int gss_get_bytes(char **ptr, const char *end, void *res, size_t len)
96 {
97         char *p, *q;
98         p = *ptr;
99         q = p + len;
100         if (q > end || q < p)
101                 return -EINVAL;
102         memcpy(res, p, len);
103         *ptr = q;
104         return 0;
105 }
106
107 int gss_get_rawobj(char **ptr, const char *end, rawobj_t *res)
108 {
109         char   *p, *q;
110         __u32   len;
111
112         p = *ptr;
113         if (gss_get_bytes(&p, end, &len, sizeof(len)))
114                 return -EINVAL;
115
116         q = p + len;
117         if (q > end || q < p)
118                 return -EINVAL;
119
120         /* Support empty objects */
121         if (len != 0) {
122                 OBD_ALLOC_LARGE(res->data, len);
123                 if (!res->data)
124                         return -ENOMEM;
125         } else {
126                 res->len = len;
127                 res->data = NULL;
128                 return 0;
129         }
130
131         res->len = len;
132         memcpy(res->data, p, len);
133         *ptr = q;
134         return 0;
135 }
136
137 int gss_get_keyblock(char **ptr, const char *end,
138                      struct gss_keyblock *kb, __u32 keysize)
139 {
140         char *buf;
141         int rc;
142
143         OBD_ALLOC_LARGE(buf, keysize);
144         if (buf == NULL)
145                 return -ENOMEM;
146
147         rc = gss_get_bytes(ptr, end, buf, keysize);
148         if (rc) {
149                 OBD_FREE_LARGE(buf, keysize);
150                 return rc;
151         }
152
153         kb->kb_key.len = keysize;
154         kb->kb_key.data = buf;
155         return 0;
156 }
157
158 /*
159  * Should be used for buffers allocated with k/vmalloc().
160  *
161  * Dispose of @sgt with gss_teardown_sgtable().
162  *
163  * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
164  * in cases where a single sg is sufficient.  No attempt to reduce the
165  * number of sgs by squeezing physically contiguous pages together is
166  * made though, for simplicity.
167  *
168  * This function is copied from the ceph filesystem code.
169  */
170 int gss_setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
171                       const void *buf, unsigned int buf_len)
172 {
173         struct scatterlist *sg;
174         const bool is_vmalloc = is_vmalloc_addr(buf);
175         unsigned int off = offset_in_page(buf);
176         unsigned int chunk_cnt = 1;
177         unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
178         int i;
179         int rc;
180
181         if (buf_len == 0) {
182                 memset(sgt, 0, sizeof(*sgt));
183                 return -EINVAL;
184         }
185
186         if (is_vmalloc) {
187                 chunk_cnt = chunk_len >> PAGE_SHIFT;
188                 chunk_len = PAGE_SIZE;
189         }
190
191         if (chunk_cnt > 1) {
192                 rc = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
193                 if (rc)
194                         return rc;
195         } else {
196                 WARN_ON_ONCE(chunk_cnt != 1);
197                 sg_init_table(prealloc_sg, 1);
198                 sgt->sgl = prealloc_sg;
199                 sgt->nents = sgt->orig_nents = 1;
200         }
201
202         for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
203                 struct page *page;
204                 unsigned int len = min(chunk_len - off, buf_len);
205
206                 if (is_vmalloc)
207                         page = vmalloc_to_page(buf);
208                 else
209                         page = virt_to_page(buf);
210
211                 sg_set_page(sg, page, len, off);
212
213                 off = 0;
214                 buf += len;
215                 buf_len -= len;
216         }
217
218         WARN_ON_ONCE(buf_len != 0);
219
220         return 0;
221 }
222
223 void gss_teardown_sgtable(struct sg_table *sgt)
224 {
225         if (sgt->orig_nents > 1)
226                 sg_free_table(sgt);
227 }
228
229 int gss_crypt_generic(struct crypto_sync_skcipher *tfm, int decrypt,
230                       const void *iv, const void *in, void *out, size_t length)
231 {
232         struct scatterlist sg;
233         struct sg_table sg_out;
234         __u8 local_iv[16] = {0};
235         __u32 ret = -EINVAL;
236         SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
237
238         LASSERT(tfm);
239
240         if (length % crypto_sync_skcipher_blocksize(tfm) != 0) {
241                 CERROR("output length %zu mismatch blocksize %d\n",
242                        length, crypto_sync_skcipher_blocksize(tfm));
243                 goto out;
244         }
245
246         if (crypto_sync_skcipher_ivsize(tfm) > ARRAY_SIZE(local_iv)) {
247                 CERROR("iv size too large %d\n",
248                         crypto_sync_skcipher_ivsize(tfm));
249                 goto out;
250         }
251
252         if (iv)
253                 memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
254
255         if (in != out)
256                 memmove(out, in, length);
257
258         ret = gss_setup_sgtable(&sg_out, &sg, out, length);
259         if (ret != 0)
260                 goto out;
261
262         skcipher_request_set_sync_tfm(req, tfm);
263         skcipher_request_set_callback(req, 0, NULL, NULL);
264         skcipher_request_set_crypt(req, &sg, &sg, length, local_iv);
265
266         if (decrypt)
267                 ret = crypto_skcipher_decrypt_iv(req, &sg, &sg, length);
268         else
269                 ret = crypto_skcipher_encrypt_iv(req, &sg, &sg, length);
270
271         skcipher_request_zero(req);
272         gss_teardown_sgtable(&sg_out);
273 out:
274         return ret;
275 }
276
277 int gss_digest_hash(struct ahash_request *req,
278                     rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
279                     int iovcnt, lnet_kiov_t *iovs)
280 {
281         struct scatterlist sg[1];
282         struct sg_table sgt;
283         int rc = 0;
284         int i;
285
286         for (i = 0; i < msgcnt; i++) {
287                 if (msgs[i].len == 0)
288                         continue;
289
290                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
291                 if (rc)
292                         return rc;
293
294                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
295                 rc = crypto_ahash_update(req);
296                 gss_teardown_sgtable(&sgt);
297                 if (rc)
298                         return rc;
299         }
300
301         for (i = 0; i < iovcnt; i++) {
302                 if (iovs[i].kiov_len == 0)
303                         continue;
304
305                 sg_init_table(sg, 1);
306                 sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
307                             iovs[i].kiov_offset);
308
309                 ahash_request_set_crypt(req, sg, NULL, iovs[i].kiov_len);
310                 rc = crypto_ahash_update(req);
311                 if (rc)
312                         return rc;
313         }
314
315         if (hdr) {
316                 rc = gss_setup_sgtable(&sgt, sg, hdr->data, hdr->len);
317                 if (rc)
318                         return rc;
319
320                 ahash_request_set_crypt(req, sg, NULL, hdr->len);
321                 rc = crypto_ahash_update(req);
322                 gss_teardown_sgtable(&sgt);
323                 if (rc)
324                         return rc;
325         }
326
327         return rc;
328 }
329
330 int gss_digest_hash_compat(struct ahash_request *req,
331                            rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
332                            int iovcnt, lnet_kiov_t *iovs)
333 {
334         struct scatterlist sg[1];
335         struct sg_table sgt;
336         int rc = 0;
337         int i;
338
339         for (i = 0; i < msgcnt; i++) {
340                 if (msgs[i].len == 0)
341                         continue;
342
343                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
344                 if (rc)
345                         return rc;
346
347                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
348                 rc = crypto_ahash_update(req);
349                 gss_teardown_sgtable(&sgt);
350                 if (rc)
351                         return rc;
352         }
353
354         for (i = 0; i < iovcnt; i++) {
355                 if (iovs[i].kiov_len == 0)
356                         continue;
357
358                 sg_init_table(sg, 1);
359                 sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
360                             iovs[i].kiov_offset);
361
362                 ahash_request_set_crypt(req, sg, NULL, iovs[i].kiov_len);
363                 rc = crypto_ahash_update(req);
364                 if (rc)
365                         return rc;
366         }
367
368         if (hdr) {
369                 rc = gss_setup_sgtable(&sgt, sg, &(hdr->len), sizeof(hdr->len));
370                 if (rc)
371                         return rc;
372
373                 ahash_request_set_crypt(req, sg, NULL, sizeof(hdr->len));
374                 rc = crypto_ahash_update(req);
375                 gss_teardown_sgtable(&sgt);
376                 if (rc)
377                         return rc;
378         }
379
380         return rc;
381 }
382
383 int gss_add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
384 {
385         int padding;
386
387         padding = (blocksize - (msg->len & (blocksize - 1))) &
388                   (blocksize - 1);
389         if (!padding)
390                 return 0;
391
392         if (msg->len + padding > msg_buflen) {
393                 CERROR("bufsize %u too small: datalen %u, padding %u\n",
394                        msg_buflen, msg->len, padding);
395                 return -EINVAL;
396         }
397
398         memset(msg->data + msg->len, padding, padding);
399         msg->len += padding;
400         return 0;
401 }
402
403 int gss_crypt_rawobjs(struct crypto_sync_skcipher *tfm, __u8 *iv,
404                       int inobj_cnt, rawobj_t *inobjs, rawobj_t *outobj,
405                       int enc)
406 {
407         struct scatterlist src;
408         struct scatterlist dst;
409         struct sg_table sg_dst;
410         struct sg_table sg_src;
411         __u8 *buf;
412         __u32 datalen = 0;
413         int i, rc;
414         SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
415
416         ENTRY;
417
418         buf = outobj->data;
419         skcipher_request_set_sync_tfm(req, tfm);
420         skcipher_request_set_callback(req, 0, NULL, NULL);
421
422         for (i = 0; i < inobj_cnt; i++) {
423                 LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
424
425                 rc = gss_setup_sgtable(&sg_src, &src, inobjs[i].data,
426                                    inobjs[i].len);
427                 if (rc != 0)
428                         RETURN(rc);
429
430                 rc = gss_setup_sgtable(&sg_dst, &dst, buf,
431                                        outobj->len - datalen);
432                 if (rc != 0) {
433                         gss_teardown_sgtable(&sg_src);
434                         RETURN(rc);
435                 }
436
437                 skcipher_request_set_crypt(req, &src, &dst, src.length, iv);
438                 if (!iv)
439                         skcipher_request_set_crypt_iv(req);
440
441                 if (enc)
442                         rc = crypto_skcipher_encrypt_iv(req, &dst, &src,
443                                                         src.length);
444                 else
445                         rc = crypto_skcipher_decrypt_iv(req, &dst, &src,
446                                                         src.length);
447
448                 gss_teardown_sgtable(&sg_src);
449                 gss_teardown_sgtable(&sg_dst);
450
451                 if (rc) {
452                         CERROR("encrypt error %d\n", rc);
453                         skcipher_request_zero(req);
454                         RETURN(rc);
455                 }
456
457                 datalen += inobjs[i].len;
458                 buf += inobjs[i].len;
459         }
460         skcipher_request_zero(req);
461
462         outobj->len = datalen;
463         RETURN(0);
464 }