Whamcloud - gitweb
LU-13098 ptlrpc: supress connection restored message
[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_blkcipher(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_blkcipher_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_blkcipher(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_blkcipher *tfm, int decrypt, const void *iv,
230                       const void *in, void *out, size_t length)
231 {
232         struct blkcipher_desc desc;
233         struct scatterlist sg;
234         struct sg_table sg_out;
235         __u8 local_iv[16] = {0};
236         __u32 ret = -EINVAL;
237
238         LASSERT(tfm);
239         desc.tfm = tfm;
240         desc.info = local_iv;
241         desc.flags = 0;
242
243         if (length % crypto_blkcipher_blocksize(tfm) != 0) {
244                 CERROR("output length %zu mismatch blocksize %d\n",
245                        length, crypto_blkcipher_blocksize(tfm));
246                 goto out;
247         }
248
249         if (crypto_blkcipher_ivsize(tfm) > ARRAY_SIZE(local_iv)) {
250                 CERROR("iv size too large %d\n", crypto_blkcipher_ivsize(tfm));
251                 goto out;
252         }
253
254         if (iv)
255                 memcpy(local_iv, iv, crypto_blkcipher_ivsize(tfm));
256
257         if (in != out)
258                 memmove(out, in, length);
259
260         ret = gss_setup_sgtable(&sg_out, &sg, out, length);
261         if (ret != 0)
262                 goto out;
263
264         if (decrypt)
265                 ret = crypto_blkcipher_decrypt_iv(&desc, &sg, &sg, length);
266         else
267                 ret = crypto_blkcipher_encrypt_iv(&desc, &sg, &sg, length);
268
269         gss_teardown_sgtable(&sg_out);
270 out:
271         return ret;
272 }
273
274 int gss_digest_hash(struct ahash_request *req,
275                     rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
276                     int iovcnt, lnet_kiov_t *iovs)
277 {
278         struct scatterlist sg[1];
279         struct sg_table sgt;
280         int rc = 0;
281         int i;
282
283         for (i = 0; i < msgcnt; i++) {
284                 if (msgs[i].len == 0)
285                         continue;
286
287                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
288                 if (rc)
289                         return rc;
290
291                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
292                 rc = crypto_ahash_update(req);
293                 gss_teardown_sgtable(&sgt);
294                 if (rc)
295                         return rc;
296         }
297
298         for (i = 0; i < iovcnt; i++) {
299                 if (iovs[i].kiov_len == 0)
300                         continue;
301
302                 sg_init_table(sg, 1);
303                 sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
304                             iovs[i].kiov_offset);
305
306                 ahash_request_set_crypt(req, sg, NULL, iovs[i].kiov_len);
307                 rc = crypto_ahash_update(req);
308                 if (rc)
309                         return rc;
310         }
311
312         if (hdr) {
313                 rc = gss_setup_sgtable(&sgt, sg, hdr->data, hdr->len);
314                 if (rc)
315                         return rc;
316
317                 ahash_request_set_crypt(req, sg, NULL, hdr->len);
318                 rc = crypto_ahash_update(req);
319                 gss_teardown_sgtable(&sgt);
320                 if (rc)
321                         return rc;
322         }
323
324         return rc;
325 }
326
327 int gss_digest_hash_compat(struct ahash_request *req,
328                            rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
329                            int iovcnt, lnet_kiov_t *iovs)
330 {
331         struct scatterlist sg[1];
332         struct sg_table sgt;
333         int rc = 0;
334         int i;
335
336         for (i = 0; i < msgcnt; i++) {
337                 if (msgs[i].len == 0)
338                         continue;
339
340                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
341                 if (rc)
342                         return rc;
343
344                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
345                 rc = crypto_ahash_update(req);
346                 gss_teardown_sgtable(&sgt);
347                 if (rc)
348                         return rc;
349         }
350
351         for (i = 0; i < iovcnt; i++) {
352                 if (iovs[i].kiov_len == 0)
353                         continue;
354
355                 sg_init_table(sg, 1);
356                 sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
357                             iovs[i].kiov_offset);
358
359                 ahash_request_set_crypt(req, sg, NULL, iovs[i].kiov_len);
360                 rc = crypto_ahash_update(req);
361                 if (rc)
362                         return rc;
363         }
364
365         if (hdr) {
366                 rc = gss_setup_sgtable(&sgt, sg, &(hdr->len), sizeof(hdr->len));
367                 if (rc)
368                         return rc;
369
370                 ahash_request_set_crypt(req, sg, NULL, sizeof(hdr->len));
371                 rc = crypto_ahash_update(req);
372                 gss_teardown_sgtable(&sgt);
373                 if (rc)
374                         return rc;
375         }
376
377         return rc;
378 }
379
380 int gss_add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
381 {
382         int padding;
383
384         padding = (blocksize - (msg->len & (blocksize - 1))) &
385                   (blocksize - 1);
386         if (!padding)
387                 return 0;
388
389         if (msg->len + padding > msg_buflen) {
390                 CERROR("bufsize %u too small: datalen %u, padding %u\n",
391                        msg_buflen, msg->len, padding);
392                 return -EINVAL;
393         }
394
395         memset(msg->data + msg->len, padding, padding);
396         msg->len += padding;
397         return 0;
398 }
399
400 int gss_crypt_rawobjs(struct crypto_blkcipher *tfm, __u8 *iv,
401                       int inobj_cnt, rawobj_t *inobjs, rawobj_t *outobj,
402                       int enc)
403 {
404         struct blkcipher_desc desc;
405         struct scatterlist src;
406         struct scatterlist dst;
407         struct sg_table sg_dst;
408         struct sg_table sg_src;
409         __u8 *buf;
410         __u32 datalen = 0;
411         int i, rc;
412         ENTRY;
413
414         buf = outobj->data;
415         desc.tfm  = tfm;
416         desc.info = iv;
417         desc.flags = 0;
418
419         for (i = 0; i < inobj_cnt; i++) {
420                 LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
421
422                 rc = gss_setup_sgtable(&sg_src, &src, inobjs[i].data,
423                                    inobjs[i].len);
424                 if (rc != 0)
425                         RETURN(rc);
426
427                 rc = gss_setup_sgtable(&sg_dst, &dst, buf,
428                                        outobj->len - datalen);
429                 if (rc != 0) {
430                         gss_teardown_sgtable(&sg_src);
431                         RETURN(rc);
432                 }
433
434                 if (iv) {
435                         if (enc)
436                                 rc = crypto_blkcipher_encrypt_iv(&desc, &dst,
437                                                                  &src,
438                                                                  src.length);
439                         else
440                                 rc = crypto_blkcipher_decrypt_iv(&desc, &dst,
441                                                                  &src,
442                                                                  src.length);
443                 } else {
444                         if (enc)
445                                 rc = crypto_blkcipher_encrypt(&desc, &dst, &src,
446                                                               src.length);
447                         else
448                                 rc = crypto_blkcipher_decrypt(&desc, &dst, &src,
449                                                               src.length);
450                 }
451
452                 gss_teardown_sgtable(&sg_src);
453                 gss_teardown_sgtable(&sg_dst);
454
455                 if (rc) {
456                         CERROR("encrypt error %d\n", rc);
457                         RETURN(rc);
458                 }
459
460                 datalen += inobjs[i].len;
461                 buf += inobjs[i].len;
462         }
463
464         outobj->len = datalen;
465         RETURN(0);
466 }