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