Whamcloud - gitweb
LU-10030 hsm: make changelog flag argument an enum
[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 cfs_crypto_hash_desc *desc,
274                     rawobj_t *hdr, int msgcnt, rawobj_t *msgs,
275                     int iovcnt, lnet_kiov_t *iovs,
276                     rawobj_t *cksum)
277 {
278         struct ahash_request *req = (struct ahash_request *)desc;
279         struct scatterlist sg[1];
280         struct sg_table sgt;
281         int rc = 0;
282         int i;
283
284         for (i = 0; i < msgcnt; i++) {
285                 if (msgs[i].len == 0)
286                         continue;
287
288                 rc = gss_setup_sgtable(&sgt, sg, msgs[i].data, msgs[i].len);
289                 if (rc)
290                         return rc;
291
292                 ahash_request_set_crypt(req, sg, NULL, msgs[i].len);
293                 rc = crypto_ahash_update(req);
294                 gss_teardown_sgtable(&sgt);
295                 if (rc)
296                         return rc;
297         }
298
299         for (i = 0; i < iovcnt; i++) {
300                 if (iovs[i].kiov_len == 0)
301                         continue;
302
303                 sg_init_table(sg, 1);
304                 sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
305                             iovs[i].kiov_offset);
306
307                 ahash_request_set_crypt(req, sg, NULL, iovs[i].kiov_len);
308                 rc = crypto_ahash_update(req);
309                 if (rc)
310                         return rc;
311         }
312
313         if (hdr) {
314                 rc = gss_setup_sgtable(&sgt, sg, hdr, sizeof(*hdr));
315                 if (rc)
316                         return rc;
317
318                 ahash_request_set_crypt(req, sg, NULL, hdr->len);
319                 rc = crypto_ahash_update(req);
320                 gss_teardown_sgtable(&sgt);
321                 if (rc)
322                         return rc;
323         }
324
325         return rc;
326 }
327
328 int gss_add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
329 {
330         int padding;
331
332         padding = (blocksize - (msg->len & (blocksize - 1))) &
333                   (blocksize - 1);
334         if (!padding)
335                 return 0;
336
337         if (msg->len + padding > msg_buflen) {
338                 CERROR("bufsize %u too small: datalen %u, padding %u\n",
339                        msg_buflen, msg->len, padding);
340                 return -EINVAL;
341         }
342
343         memset(msg->data + msg->len, padding, padding);
344         msg->len += padding;
345         return 0;
346 }
347
348 int gss_crypt_rawobjs(struct crypto_blkcipher *tfm, __u8 *iv,
349                       int inobj_cnt, rawobj_t *inobjs, rawobj_t *outobj,
350                       int enc)
351 {
352         struct blkcipher_desc desc;
353         struct scatterlist src;
354         struct scatterlist dst;
355         struct sg_table sg_dst;
356         struct sg_table sg_src;
357         __u8 *buf;
358         __u32 datalen = 0;
359         int i, rc;
360         ENTRY;
361
362         buf = outobj->data;
363         desc.tfm  = tfm;
364         desc.info = iv;
365         desc.flags = 0;
366
367         for (i = 0; i < inobj_cnt; i++) {
368                 LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
369
370                 rc = gss_setup_sgtable(&sg_src, &src, inobjs[i].data,
371                                    inobjs[i].len);
372                 if (rc != 0)
373                         RETURN(rc);
374
375                 rc = gss_setup_sgtable(&sg_dst, &dst, buf,
376                                        outobj->len - datalen);
377                 if (rc != 0) {
378                         gss_teardown_sgtable(&sg_src);
379                         RETURN(rc);
380                 }
381
382                 if (iv) {
383                         if (enc)
384                                 rc = crypto_blkcipher_encrypt_iv(&desc, &dst,
385                                                                  &src,
386                                                                  src.length);
387                         else
388                                 rc = crypto_blkcipher_decrypt_iv(&desc, &dst,
389                                                                  &src,
390                                                                  src.length);
391                 } else {
392                         if (enc)
393                                 rc = crypto_blkcipher_encrypt(&desc, &dst, &src,
394                                                               src.length);
395                         else
396                                 rc = crypto_blkcipher_decrypt(&desc, &dst, &src,
397                                                               src.length);
398                 }
399
400                 gss_teardown_sgtable(&sg_src);
401                 gss_teardown_sgtable(&sg_dst);
402
403                 if (rc) {
404                         CERROR("encrypt error %d\n", rc);
405                         RETURN(rc);
406                 }
407
408                 datalen += inobjs[i].len;
409                 buf += inobjs[i].len;
410         }
411
412         outobj->len = datalen;
413         RETURN(0);
414 }