Whamcloud - gitweb
4231a0a56833cab9857808316085adf7032f4967
[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         memcpy(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                     rawobj_t *cksum)
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, sizeof(*hdr));
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_add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
328 {
329         int padding;
330
331         padding = (blocksize - (msg->len & (blocksize - 1))) &
332                   (blocksize - 1);
333         if (!padding)
334                 return 0;
335
336         if (msg->len + padding > msg_buflen) {
337                 CERROR("bufsize %u too small: datalen %u, padding %u\n",
338                        msg_buflen, msg->len, padding);
339                 return -EINVAL;
340         }
341
342         memset(msg->data + msg->len, padding, padding);
343         msg->len += padding;
344         return 0;
345 }
346
347 int gss_crypt_rawobjs(struct crypto_blkcipher *tfm, __u8 *iv,
348                       int inobj_cnt, rawobj_t *inobjs, rawobj_t *outobj,
349                       int enc)
350 {
351         struct blkcipher_desc desc;
352         struct scatterlist src;
353         struct scatterlist dst;
354         struct sg_table sg_dst;
355         struct sg_table sg_src;
356         __u8 *buf;
357         __u32 datalen = 0;
358         int i, rc;
359         ENTRY;
360
361         buf = outobj->data;
362         desc.tfm  = tfm;
363         desc.info = iv;
364         desc.flags = 0;
365
366         for (i = 0; i < inobj_cnt; i++) {
367                 LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
368
369                 rc = gss_setup_sgtable(&sg_src, &src, inobjs[i].data,
370                                    inobjs[i].len);
371                 if (rc != 0)
372                         RETURN(rc);
373
374                 rc = gss_setup_sgtable(&sg_dst, &dst, buf,
375                                        outobj->len - datalen);
376                 if (rc != 0) {
377                         gss_teardown_sgtable(&sg_src);
378                         RETURN(rc);
379                 }
380
381                 if (iv) {
382                         if (enc)
383                                 rc = crypto_blkcipher_encrypt_iv(&desc, &dst,
384                                                                  &src,
385                                                                  src.length);
386                         else
387                                 rc = crypto_blkcipher_decrypt_iv(&desc, &dst,
388                                                                  &src,
389                                                                  src.length);
390                 } else {
391                         if (enc)
392                                 rc = crypto_blkcipher_encrypt(&desc, &dst, &src,
393                                                               src.length);
394                         else
395                                 rc = crypto_blkcipher_decrypt(&desc, &dst, &src,
396                                                               src.length);
397                 }
398
399                 gss_teardown_sgtable(&sg_src);
400                 gss_teardown_sgtable(&sg_dst);
401
402                 if (rc) {
403                         CERROR("encrypt error %d\n", rc);
404                         RETURN(rc);
405                 }
406
407                 datalen += inobjs[i].len;
408                 buf += inobjs[i].len;
409         }
410
411         outobj->len = datalen;
412         RETURN(0);
413 }