Commit | Line | Data |
---|---|---|
d7e09d03 PT |
1 | /* |
2 | * Modifications for Lustre | |
3 | * | |
4 | * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. | |
5 | * | |
6 | * Copyright (c) 2011, 2012, Intel Corporation. | |
7 | * | |
8 | * Author: Eric Mei <ericm@clusterfs.com> | |
9 | */ | |
10 | ||
11 | /* | |
12 | * linux/net/sunrpc/gss_krb5_mech.c | |
13 | * linux/net/sunrpc/gss_krb5_crypto.c | |
14 | * linux/net/sunrpc/gss_krb5_seal.c | |
15 | * linux/net/sunrpc/gss_krb5_seqnum.c | |
16 | * linux/net/sunrpc/gss_krb5_unseal.c | |
17 | * | |
18 | * Copyright (c) 2001 The Regents of the University of Michigan. | |
19 | * All rights reserved. | |
20 | * | |
21 | * Andy Adamson <andros@umich.edu> | |
22 | * J. Bruce Fields <bfields@umich.edu> | |
23 | * | |
24 | * Redistribution and use in source and binary forms, with or without | |
25 | * modification, are permitted provided that the following conditions | |
26 | * are met: | |
27 | * | |
28 | * 1. Redistributions of source code must retain the above copyright | |
29 | * notice, this list of conditions and the following disclaimer. | |
30 | * 2. Redistributions in binary form must reproduce the above copyright | |
31 | * notice, this list of conditions and the following disclaimer in the | |
32 | * documentation and/or other materials provided with the distribution. | |
33 | * 3. Neither the name of the University nor the names of its | |
34 | * contributors may be used to endorse or promote products derived | |
35 | * from this software without specific prior written permission. | |
36 | * | |
37 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
38 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
39 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
40 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
41 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
42 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
43 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
44 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
45 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
46 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
47 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 | * | |
49 | */ | |
50 | ||
51 | #define DEBUG_SUBSYSTEM S_SEC | |
52 | #include <linux/init.h> | |
53 | #include <linux/module.h> | |
54 | #include <linux/slab.h> | |
55 | #include <linux/crypto.h> | |
56 | #include <linux/mutex.h> | |
57 | ||
58 | #include <obd.h> | |
59 | #include <obd_class.h> | |
60 | #include <obd_support.h> | |
61 | #include <lustre/lustre_idl.h> | |
62 | #include <lustre_net.h> | |
63 | #include <lustre_import.h> | |
64 | #include <lustre_sec.h> | |
65 | ||
66 | #include "gss_err.h" | |
67 | #include "gss_internal.h" | |
68 | #include "gss_api.h" | |
69 | #include "gss_asn1.h" | |
70 | #include "gss_krb5.h" | |
71 | ||
72 | static spinlock_t krb5_seq_lock; | |
73 | ||
74 | struct krb5_enctype { | |
75 | char *ke_dispname; | |
76 | char *ke_enc_name; /* linux tfm name */ | |
77 | char *ke_hash_name; /* linux tfm name */ | |
78 | int ke_enc_mode; /* linux tfm mode */ | |
79 | int ke_hash_size; /* checksum size */ | |
80 | int ke_conf_size; /* confounder size */ | |
81 | unsigned int ke_hash_hmac:1; /* is hmac? */ | |
82 | }; | |
83 | ||
84 | /* | |
85 | * NOTE: for aes128-cts and aes256-cts, MIT implementation use CTS encryption. | |
86 | * but currently we simply CBC with padding, because linux doesn't support CTS | |
87 | * yet. this need to be fixed in the future. | |
88 | */ | |
89 | static struct krb5_enctype enctypes[] = { | |
90 | [ENCTYPE_DES_CBC_RAW] = { /* des-cbc-md5 */ | |
91 | "des-cbc-md5", | |
92 | "cbc(des)", | |
93 | "md5", | |
94 | 0, | |
95 | 16, | |
96 | 8, | |
97 | 0, | |
98 | }, | |
99 | [ENCTYPE_DES3_CBC_RAW] = { /* des3-hmac-sha1 */ | |
100 | "des3-hmac-sha1", | |
101 | "cbc(des3_ede)", | |
102 | "hmac(sha1)", | |
103 | 0, | |
104 | 20, | |
105 | 8, | |
106 | 1, | |
107 | }, | |
108 | [ENCTYPE_AES128_CTS_HMAC_SHA1_96] = { /* aes128-cts */ | |
109 | "aes128-cts-hmac-sha1-96", | |
110 | "cbc(aes)", | |
111 | "hmac(sha1)", | |
112 | 0, | |
113 | 12, | |
114 | 16, | |
115 | 1, | |
116 | }, | |
117 | [ENCTYPE_AES256_CTS_HMAC_SHA1_96] = { /* aes256-cts */ | |
118 | "aes256-cts-hmac-sha1-96", | |
119 | "cbc(aes)", | |
120 | "hmac(sha1)", | |
121 | 0, | |
122 | 12, | |
123 | 16, | |
124 | 1, | |
125 | }, | |
126 | [ENCTYPE_ARCFOUR_HMAC] = { /* arcfour-hmac-md5 */ | |
127 | "arcfour-hmac-md5", | |
128 | "ecb(arc4)", | |
129 | "hmac(md5)", | |
130 | 0, | |
131 | 16, | |
132 | 8, | |
133 | 1, | |
134 | }, | |
135 | }; | |
136 | ||
137 | #define MAX_ENCTYPES sizeof(enctypes)/sizeof(struct krb5_enctype) | |
138 | ||
139 | static const char * enctype2str(__u32 enctype) | |
140 | { | |
141 | if (enctype < MAX_ENCTYPES && enctypes[enctype].ke_dispname) | |
142 | return enctypes[enctype].ke_dispname; | |
143 | ||
144 | return "unknown"; | |
145 | } | |
146 | ||
147 | static | |
148 | int keyblock_init(struct krb5_keyblock *kb, char *alg_name, int alg_mode) | |
149 | { | |
150 | kb->kb_tfm = ll_crypto_alloc_blkcipher(alg_name, alg_mode, 0); | |
151 | if (IS_ERR(kb->kb_tfm)) { | |
152 | CERROR("failed to alloc tfm: %s, mode %d\n", | |
153 | alg_name, alg_mode); | |
154 | return -1; | |
155 | } | |
156 | ||
157 | if (ll_crypto_blkcipher_setkey(kb->kb_tfm, kb->kb_key.data, kb->kb_key.len)) { | |
158 | CERROR("failed to set %s key, len %d\n", | |
159 | alg_name, kb->kb_key.len); | |
160 | return -1; | |
161 | } | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static | |
167 | int krb5_init_keys(struct krb5_ctx *kctx) | |
168 | { | |
169 | struct krb5_enctype *ke; | |
170 | ||
171 | if (kctx->kc_enctype >= MAX_ENCTYPES || | |
172 | enctypes[kctx->kc_enctype].ke_hash_size == 0) { | |
173 | CERROR("unsupported enctype %x\n", kctx->kc_enctype); | |
174 | return -1; | |
175 | } | |
176 | ||
177 | ke = &enctypes[kctx->kc_enctype]; | |
178 | ||
179 | /* tfm arc4 is stateful, user should alloc-use-free by his own */ | |
180 | if (kctx->kc_enctype != ENCTYPE_ARCFOUR_HMAC && | |
181 | keyblock_init(&kctx->kc_keye, ke->ke_enc_name, ke->ke_enc_mode)) | |
182 | return -1; | |
183 | ||
184 | /* tfm hmac is stateful, user should alloc-use-free by his own */ | |
185 | if (ke->ke_hash_hmac == 0 && | |
186 | keyblock_init(&kctx->kc_keyi, ke->ke_enc_name, ke->ke_enc_mode)) | |
187 | return -1; | |
188 | if (ke->ke_hash_hmac == 0 && | |
189 | keyblock_init(&kctx->kc_keyc, ke->ke_enc_name, ke->ke_enc_mode)) | |
190 | return -1; | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static | |
196 | void keyblock_free(struct krb5_keyblock *kb) | |
197 | { | |
198 | rawobj_free(&kb->kb_key); | |
199 | if (kb->kb_tfm) | |
200 | ll_crypto_free_blkcipher(kb->kb_tfm); | |
201 | } | |
202 | ||
203 | static | |
204 | int keyblock_dup(struct krb5_keyblock *new, struct krb5_keyblock *kb) | |
205 | { | |
206 | return rawobj_dup(&new->kb_key, &kb->kb_key); | |
207 | } | |
208 | ||
209 | static | |
210 | int get_bytes(char **ptr, const char *end, void *res, int len) | |
211 | { | |
212 | char *p, *q; | |
213 | p = *ptr; | |
214 | q = p + len; | |
215 | if (q > end || q < p) | |
216 | return -1; | |
217 | memcpy(res, p, len); | |
218 | *ptr = q; | |
219 | return 0; | |
220 | } | |
221 | ||
222 | static | |
223 | int get_rawobj(char **ptr, const char *end, rawobj_t *res) | |
224 | { | |
225 | char *p, *q; | |
226 | __u32 len; | |
227 | ||
228 | p = *ptr; | |
229 | if (get_bytes(&p, end, &len, sizeof(len))) | |
230 | return -1; | |
231 | ||
232 | q = p + len; | |
233 | if (q > end || q < p) | |
234 | return -1; | |
235 | ||
236 | OBD_ALLOC_LARGE(res->data, len); | |
237 | if (!res->data) | |
238 | return -1; | |
239 | ||
240 | res->len = len; | |
241 | memcpy(res->data, p, len); | |
242 | *ptr = q; | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static | |
247 | int get_keyblock(char **ptr, const char *end, | |
248 | struct krb5_keyblock *kb, __u32 keysize) | |
249 | { | |
250 | char *buf; | |
251 | ||
252 | OBD_ALLOC_LARGE(buf, keysize); | |
253 | if (buf == NULL) | |
254 | return -1; | |
255 | ||
256 | if (get_bytes(ptr, end, buf, keysize)) { | |
257 | OBD_FREE_LARGE(buf, keysize); | |
258 | return -1; | |
259 | } | |
260 | ||
261 | kb->kb_key.len = keysize; | |
262 | kb->kb_key.data = buf; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | static | |
267 | void delete_context_kerberos(struct krb5_ctx *kctx) | |
268 | { | |
269 | rawobj_free(&kctx->kc_mech_used); | |
270 | ||
271 | keyblock_free(&kctx->kc_keye); | |
272 | keyblock_free(&kctx->kc_keyi); | |
273 | keyblock_free(&kctx->kc_keyc); | |
274 | } | |
275 | ||
276 | static | |
277 | __u32 import_context_rfc1964(struct krb5_ctx *kctx, char *p, char *end) | |
278 | { | |
279 | unsigned int tmp_uint, keysize; | |
280 | ||
281 | /* seed_init flag */ | |
282 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) | |
283 | goto out_err; | |
284 | kctx->kc_seed_init = (tmp_uint != 0); | |
285 | ||
286 | /* seed */ | |
287 | if (get_bytes(&p, end, kctx->kc_seed, sizeof(kctx->kc_seed))) | |
288 | goto out_err; | |
289 | ||
290 | /* sign/seal algorithm, not really used now */ | |
291 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) || | |
292 | get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) | |
293 | goto out_err; | |
294 | ||
295 | /* end time */ | |
296 | if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime))) | |
297 | goto out_err; | |
298 | ||
299 | /* seq send */ | |
300 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) | |
301 | goto out_err; | |
302 | kctx->kc_seq_send = tmp_uint; | |
303 | ||
304 | /* mech oid */ | |
305 | if (get_rawobj(&p, end, &kctx->kc_mech_used)) | |
306 | goto out_err; | |
307 | ||
308 | /* old style enc/seq keys in format: | |
309 | * - enctype (u32) | |
310 | * - keysize (u32) | |
311 | * - keydata | |
312 | * we decompose them to fit into the new context | |
313 | */ | |
314 | ||
315 | /* enc key */ | |
316 | if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype))) | |
317 | goto out_err; | |
318 | ||
319 | if (get_bytes(&p, end, &keysize, sizeof(keysize))) | |
320 | goto out_err; | |
321 | ||
322 | if (get_keyblock(&p, end, &kctx->kc_keye, keysize)) | |
323 | goto out_err; | |
324 | ||
325 | /* seq key */ | |
326 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) || | |
327 | tmp_uint != kctx->kc_enctype) | |
328 | goto out_err; | |
329 | ||
330 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) || | |
331 | tmp_uint != keysize) | |
332 | goto out_err; | |
333 | ||
334 | if (get_keyblock(&p, end, &kctx->kc_keyc, keysize)) | |
335 | goto out_err; | |
336 | ||
337 | /* old style fallback */ | |
338 | if (keyblock_dup(&kctx->kc_keyi, &kctx->kc_keyc)) | |
339 | goto out_err; | |
340 | ||
341 | if (p != end) | |
342 | goto out_err; | |
343 | ||
c4f39553 | 344 | CDEBUG(D_SEC, "successfully imported rfc1964 context\n"); |
d7e09d03 PT |
345 | return 0; |
346 | out_err: | |
347 | return GSS_S_FAILURE; | |
348 | } | |
349 | ||
350 | /* Flags for version 2 context flags */ | |
351 | #define KRB5_CTX_FLAG_INITIATOR 0x00000001 | |
352 | #define KRB5_CTX_FLAG_CFX 0x00000002 | |
353 | #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 | |
354 | ||
355 | static | |
356 | __u32 import_context_rfc4121(struct krb5_ctx *kctx, char *p, char *end) | |
357 | { | |
358 | unsigned int tmp_uint, keysize; | |
359 | ||
360 | /* end time */ | |
361 | if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime))) | |
362 | goto out_err; | |
363 | ||
364 | /* flags */ | |
365 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) | |
366 | goto out_err; | |
367 | ||
368 | if (tmp_uint & KRB5_CTX_FLAG_INITIATOR) | |
369 | kctx->kc_initiate = 1; | |
370 | if (tmp_uint & KRB5_CTX_FLAG_CFX) | |
371 | kctx->kc_cfx = 1; | |
372 | if (tmp_uint & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) | |
373 | kctx->kc_have_acceptor_subkey = 1; | |
374 | ||
375 | /* seq send */ | |
376 | if (get_bytes(&p, end, &kctx->kc_seq_send, sizeof(kctx->kc_seq_send))) | |
377 | goto out_err; | |
378 | ||
379 | /* enctype */ | |
380 | if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype))) | |
381 | goto out_err; | |
382 | ||
383 | /* size of each key */ | |
384 | if (get_bytes(&p, end, &keysize, sizeof(keysize))) | |
385 | goto out_err; | |
386 | ||
387 | /* number of keys - should always be 3 */ | |
388 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) | |
389 | goto out_err; | |
390 | ||
391 | if (tmp_uint != 3) { | |
392 | CERROR("Invalid number of keys: %u\n", tmp_uint); | |
393 | goto out_err; | |
394 | } | |
395 | ||
396 | /* ke */ | |
397 | if (get_keyblock(&p, end, &kctx->kc_keye, keysize)) | |
398 | goto out_err; | |
399 | /* ki */ | |
400 | if (get_keyblock(&p, end, &kctx->kc_keyi, keysize)) | |
401 | goto out_err; | |
402 | /* ki */ | |
403 | if (get_keyblock(&p, end, &kctx->kc_keyc, keysize)) | |
404 | goto out_err; | |
405 | ||
c4f39553 | 406 | CDEBUG(D_SEC, "successfully imported v2 context\n"); |
d7e09d03 PT |
407 | return 0; |
408 | out_err: | |
409 | return GSS_S_FAILURE; | |
410 | } | |
411 | ||
412 | /* | |
413 | * The whole purpose here is trying to keep user level gss context parsing | |
414 | * from nfs-utils unchanged as possible as we can, they are not quite mature | |
415 | * yet, and many stuff still not clear, like heimdal etc. | |
416 | */ | |
417 | static | |
418 | __u32 gss_import_sec_context_kerberos(rawobj_t *inbuf, | |
419 | struct gss_ctx *gctx) | |
420 | { | |
421 | struct krb5_ctx *kctx; | |
422 | char *p = (char *) inbuf->data; | |
423 | char *end = (char *) (inbuf->data + inbuf->len); | |
424 | unsigned int tmp_uint, rc; | |
425 | ||
426 | if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) { | |
427 | CERROR("Fail to read version\n"); | |
428 | return GSS_S_FAILURE; | |
429 | } | |
430 | ||
431 | /* only support 0, 1 for the moment */ | |
432 | if (tmp_uint > 2) { | |
433 | CERROR("Invalid version %u\n", tmp_uint); | |
434 | return GSS_S_FAILURE; | |
435 | } | |
436 | ||
437 | OBD_ALLOC_PTR(kctx); | |
438 | if (!kctx) | |
439 | return GSS_S_FAILURE; | |
440 | ||
441 | if (tmp_uint == 0 || tmp_uint == 1) { | |
442 | kctx->kc_initiate = tmp_uint; | |
443 | rc = import_context_rfc1964(kctx, p, end); | |
444 | } else { | |
445 | rc = import_context_rfc4121(kctx, p, end); | |
446 | } | |
447 | ||
448 | if (rc == 0) | |
449 | rc = krb5_init_keys(kctx); | |
450 | ||
451 | if (rc) { | |
452 | delete_context_kerberos(kctx); | |
453 | OBD_FREE_PTR(kctx); | |
454 | ||
455 | return GSS_S_FAILURE; | |
456 | } | |
457 | ||
458 | gctx->internal_ctx_id = kctx; | |
459 | return GSS_S_COMPLETE; | |
460 | } | |
461 | ||
462 | static | |
463 | __u32 gss_copy_reverse_context_kerberos(struct gss_ctx *gctx, | |
464 | struct gss_ctx *gctx_new) | |
465 | { | |
466 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
467 | struct krb5_ctx *knew; | |
468 | ||
469 | OBD_ALLOC_PTR(knew); | |
470 | if (!knew) | |
471 | return GSS_S_FAILURE; | |
472 | ||
473 | knew->kc_initiate = kctx->kc_initiate ? 0 : 1; | |
474 | knew->kc_cfx = kctx->kc_cfx; | |
475 | knew->kc_seed_init = kctx->kc_seed_init; | |
476 | knew->kc_have_acceptor_subkey = kctx->kc_have_acceptor_subkey; | |
477 | knew->kc_endtime = kctx->kc_endtime; | |
478 | ||
479 | memcpy(knew->kc_seed, kctx->kc_seed, sizeof(kctx->kc_seed)); | |
480 | knew->kc_seq_send = kctx->kc_seq_recv; | |
481 | knew->kc_seq_recv = kctx->kc_seq_send; | |
482 | knew->kc_enctype = kctx->kc_enctype; | |
483 | ||
484 | if (rawobj_dup(&knew->kc_mech_used, &kctx->kc_mech_used)) | |
485 | goto out_err; | |
486 | ||
487 | if (keyblock_dup(&knew->kc_keye, &kctx->kc_keye)) | |
488 | goto out_err; | |
489 | if (keyblock_dup(&knew->kc_keyi, &kctx->kc_keyi)) | |
490 | goto out_err; | |
491 | if (keyblock_dup(&knew->kc_keyc, &kctx->kc_keyc)) | |
492 | goto out_err; | |
493 | if (krb5_init_keys(knew)) | |
494 | goto out_err; | |
495 | ||
496 | gctx_new->internal_ctx_id = knew; | |
c4f39553 | 497 | CDEBUG(D_SEC, "successfully copied reverse context\n"); |
d7e09d03 PT |
498 | return GSS_S_COMPLETE; |
499 | ||
500 | out_err: | |
501 | delete_context_kerberos(knew); | |
502 | OBD_FREE_PTR(knew); | |
503 | return GSS_S_FAILURE; | |
504 | } | |
505 | ||
506 | static | |
507 | __u32 gss_inquire_context_kerberos(struct gss_ctx *gctx, | |
508 | unsigned long *endtime) | |
509 | { | |
510 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
511 | ||
512 | *endtime = (unsigned long) ((__u32) kctx->kc_endtime); | |
513 | return GSS_S_COMPLETE; | |
514 | } | |
515 | ||
516 | static | |
517 | void gss_delete_sec_context_kerberos(void *internal_ctx) | |
518 | { | |
519 | struct krb5_ctx *kctx = internal_ctx; | |
520 | ||
521 | delete_context_kerberos(kctx); | |
522 | OBD_FREE_PTR(kctx); | |
523 | } | |
524 | ||
525 | static | |
526 | void buf_to_sg(struct scatterlist *sg, void *ptr, int len) | |
527 | { | |
528 | sg_set_buf(sg, ptr, len); | |
529 | } | |
530 | ||
531 | static | |
532 | __u32 krb5_encrypt(struct ll_crypto_cipher *tfm, | |
533 | int decrypt, | |
534 | void * iv, | |
535 | void * in, | |
536 | void * out, | |
537 | int length) | |
538 | { | |
539 | struct blkcipher_desc desc; | |
540 | struct scatterlist sg; | |
541 | __u8 local_iv[16] = {0}; | |
542 | __u32 ret = -EINVAL; | |
543 | ||
544 | LASSERT(tfm); | |
545 | desc.tfm = tfm; | |
546 | desc.info = local_iv; | |
547 | desc.flags= 0; | |
548 | ||
549 | if (length % ll_crypto_blkcipher_blocksize(tfm) != 0) { | |
550 | CERROR("output length %d mismatch blocksize %d\n", | |
551 | length, ll_crypto_blkcipher_blocksize(tfm)); | |
552 | goto out; | |
553 | } | |
554 | ||
555 | if (ll_crypto_blkcipher_ivsize(tfm) > 16) { | |
556 | CERROR("iv size too large %d\n", ll_crypto_blkcipher_ivsize(tfm)); | |
557 | goto out; | |
558 | } | |
559 | ||
560 | if (iv) | |
561 | memcpy(local_iv, iv, ll_crypto_blkcipher_ivsize(tfm)); | |
562 | ||
563 | memcpy(out, in, length); | |
564 | buf_to_sg(&sg, out, length); | |
565 | ||
566 | if (decrypt) | |
567 | ret = ll_crypto_blkcipher_decrypt_iv(&desc, &sg, &sg, length); | |
568 | else | |
569 | ret = ll_crypto_blkcipher_encrypt_iv(&desc, &sg, &sg, length); | |
570 | ||
571 | out: | |
572 | return(ret); | |
573 | } | |
574 | ||
575 | ||
576 | static inline | |
577 | int krb5_digest_hmac(struct ll_crypto_hash *tfm, | |
578 | rawobj_t *key, | |
579 | struct krb5_header *khdr, | |
580 | int msgcnt, rawobj_t *msgs, | |
581 | int iovcnt, lnet_kiov_t *iovs, | |
582 | rawobj_t *cksum) | |
583 | { | |
584 | struct hash_desc desc; | |
585 | struct scatterlist sg[1]; | |
586 | int i; | |
587 | ||
588 | ll_crypto_hash_setkey(tfm, key->data, key->len); | |
589 | desc.tfm = tfm; | |
590 | desc.flags= 0; | |
591 | ||
592 | ll_crypto_hash_init(&desc); | |
593 | ||
594 | for (i = 0; i < msgcnt; i++) { | |
595 | if (msgs[i].len == 0) | |
596 | continue; | |
597 | buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len); | |
598 | ll_crypto_hash_update(&desc, sg, msgs[i].len); | |
599 | } | |
600 | ||
601 | for (i = 0; i < iovcnt; i++) { | |
602 | if (iovs[i].kiov_len == 0) | |
603 | continue; | |
604 | ||
605 | sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len, | |
606 | iovs[i].kiov_offset); | |
607 | ll_crypto_hash_update(&desc, sg, iovs[i].kiov_len); | |
608 | } | |
609 | ||
610 | if (khdr) { | |
611 | buf_to_sg(sg, (char *) khdr, sizeof(*khdr)); | |
612 | ll_crypto_hash_update(&desc, sg, sizeof(*khdr)); | |
613 | } | |
614 | ||
615 | return ll_crypto_hash_final(&desc, cksum->data); | |
616 | } | |
617 | ||
618 | ||
619 | static inline | |
620 | int krb5_digest_norm(struct ll_crypto_hash *tfm, | |
621 | struct krb5_keyblock *kb, | |
622 | struct krb5_header *khdr, | |
623 | int msgcnt, rawobj_t *msgs, | |
624 | int iovcnt, lnet_kiov_t *iovs, | |
625 | rawobj_t *cksum) | |
626 | { | |
627 | struct hash_desc desc; | |
628 | struct scatterlist sg[1]; | |
629 | int i; | |
630 | ||
631 | LASSERT(kb->kb_tfm); | |
632 | desc.tfm = tfm; | |
633 | desc.flags= 0; | |
634 | ||
635 | ll_crypto_hash_init(&desc); | |
636 | ||
637 | for (i = 0; i < msgcnt; i++) { | |
638 | if (msgs[i].len == 0) | |
639 | continue; | |
640 | buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len); | |
641 | ll_crypto_hash_update(&desc, sg, msgs[i].len); | |
642 | } | |
643 | ||
644 | for (i = 0; i < iovcnt; i++) { | |
645 | if (iovs[i].kiov_len == 0) | |
646 | continue; | |
647 | ||
648 | sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len, | |
649 | iovs[i].kiov_offset); | |
650 | ll_crypto_hash_update(&desc, sg, iovs[i].kiov_len); | |
651 | } | |
652 | ||
653 | if (khdr) { | |
654 | buf_to_sg(sg, (char *) khdr, sizeof(*khdr)); | |
655 | ll_crypto_hash_update(&desc, sg, sizeof(*khdr)); | |
656 | } | |
657 | ||
658 | ll_crypto_hash_final(&desc, cksum->data); | |
659 | ||
660 | return krb5_encrypt(kb->kb_tfm, 0, NULL, cksum->data, | |
661 | cksum->data, cksum->len); | |
662 | } | |
663 | ||
664 | /* | |
665 | * compute (keyed/keyless) checksum against the plain text which appended | |
666 | * with krb5 wire token header. | |
667 | */ | |
668 | static | |
669 | __s32 krb5_make_checksum(__u32 enctype, | |
670 | struct krb5_keyblock *kb, | |
671 | struct krb5_header *khdr, | |
672 | int msgcnt, rawobj_t *msgs, | |
673 | int iovcnt, lnet_kiov_t *iovs, | |
674 | rawobj_t *cksum) | |
675 | { | |
676 | struct krb5_enctype *ke = &enctypes[enctype]; | |
677 | struct ll_crypto_hash *tfm; | |
678 | __u32 code = GSS_S_FAILURE; | |
679 | int rc; | |
680 | ||
681 | if (!(tfm = ll_crypto_alloc_hash(ke->ke_hash_name, 0, 0))) { | |
682 | CERROR("failed to alloc TFM: %s\n", ke->ke_hash_name); | |
683 | return GSS_S_FAILURE; | |
684 | } | |
685 | ||
686 | cksum->len = ll_crypto_hash_digestsize(tfm); | |
687 | OBD_ALLOC_LARGE(cksum->data, cksum->len); | |
688 | if (!cksum->data) { | |
689 | cksum->len = 0; | |
690 | goto out_tfm; | |
691 | } | |
692 | ||
693 | if (ke->ke_hash_hmac) | |
694 | rc = krb5_digest_hmac(tfm, &kb->kb_key, | |
695 | khdr, msgcnt, msgs, iovcnt, iovs, cksum); | |
696 | else | |
697 | rc = krb5_digest_norm(tfm, kb, | |
698 | khdr, msgcnt, msgs, iovcnt, iovs, cksum); | |
699 | ||
700 | if (rc == 0) | |
701 | code = GSS_S_COMPLETE; | |
702 | out_tfm: | |
703 | ll_crypto_free_hash(tfm); | |
704 | return code; | |
705 | } | |
706 | ||
707 | static void fill_krb5_header(struct krb5_ctx *kctx, | |
708 | struct krb5_header *khdr, | |
709 | int privacy) | |
710 | { | |
711 | unsigned char acceptor_flag; | |
712 | ||
713 | acceptor_flag = kctx->kc_initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; | |
714 | ||
715 | if (privacy) { | |
716 | khdr->kh_tok_id = cpu_to_be16(KG_TOK_WRAP_MSG); | |
717 | khdr->kh_flags = acceptor_flag | FLAG_WRAP_CONFIDENTIAL; | |
718 | khdr->kh_ec = cpu_to_be16(0); | |
719 | khdr->kh_rrc = cpu_to_be16(0); | |
720 | } else { | |
721 | khdr->kh_tok_id = cpu_to_be16(KG_TOK_MIC_MSG); | |
722 | khdr->kh_flags = acceptor_flag; | |
723 | khdr->kh_ec = cpu_to_be16(0xffff); | |
724 | khdr->kh_rrc = cpu_to_be16(0xffff); | |
725 | } | |
726 | ||
727 | khdr->kh_filler = 0xff; | |
728 | spin_lock(&krb5_seq_lock); | |
729 | khdr->kh_seq = cpu_to_be64(kctx->kc_seq_send++); | |
730 | spin_unlock(&krb5_seq_lock); | |
731 | } | |
732 | ||
733 | static __u32 verify_krb5_header(struct krb5_ctx *kctx, | |
734 | struct krb5_header *khdr, | |
735 | int privacy) | |
736 | { | |
737 | unsigned char acceptor_flag; | |
738 | __u16 tok_id, ec_rrc; | |
739 | ||
740 | acceptor_flag = kctx->kc_initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; | |
741 | ||
742 | if (privacy) { | |
743 | tok_id = KG_TOK_WRAP_MSG; | |
744 | ec_rrc = 0x0; | |
745 | } else { | |
746 | tok_id = KG_TOK_MIC_MSG; | |
747 | ec_rrc = 0xffff; | |
748 | } | |
749 | ||
750 | /* sanity checks */ | |
751 | if (be16_to_cpu(khdr->kh_tok_id) != tok_id) { | |
752 | CERROR("bad token id\n"); | |
753 | return GSS_S_DEFECTIVE_TOKEN; | |
754 | } | |
755 | if ((khdr->kh_flags & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { | |
756 | CERROR("bad direction flag\n"); | |
757 | return GSS_S_BAD_SIG; | |
758 | } | |
759 | if (privacy && (khdr->kh_flags & FLAG_WRAP_CONFIDENTIAL) == 0) { | |
760 | CERROR("missing confidential flag\n"); | |
761 | return GSS_S_BAD_SIG; | |
762 | } | |
763 | if (khdr->kh_filler != 0xff) { | |
764 | CERROR("bad filler\n"); | |
765 | return GSS_S_DEFECTIVE_TOKEN; | |
766 | } | |
767 | if (be16_to_cpu(khdr->kh_ec) != ec_rrc || | |
768 | be16_to_cpu(khdr->kh_rrc) != ec_rrc) { | |
769 | CERROR("bad EC or RRC\n"); | |
770 | return GSS_S_DEFECTIVE_TOKEN; | |
771 | } | |
772 | return GSS_S_COMPLETE; | |
773 | } | |
774 | ||
775 | static | |
776 | __u32 gss_get_mic_kerberos(struct gss_ctx *gctx, | |
777 | int msgcnt, | |
778 | rawobj_t *msgs, | |
779 | int iovcnt, | |
780 | lnet_kiov_t *iovs, | |
781 | rawobj_t *token) | |
782 | { | |
783 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
784 | struct krb5_enctype *ke = &enctypes[kctx->kc_enctype]; | |
785 | struct krb5_header *khdr; | |
786 | rawobj_t cksum = RAWOBJ_EMPTY; | |
787 | ||
788 | /* fill krb5 header */ | |
789 | LASSERT(token->len >= sizeof(*khdr)); | |
790 | khdr = (struct krb5_header *) token->data; | |
791 | fill_krb5_header(kctx, khdr, 0); | |
792 | ||
793 | /* checksum */ | |
794 | if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc, | |
795 | khdr, msgcnt, msgs, iovcnt, iovs, &cksum)) | |
796 | return GSS_S_FAILURE; | |
797 | ||
798 | LASSERT(cksum.len >= ke->ke_hash_size); | |
799 | LASSERT(token->len >= sizeof(*khdr) + ke->ke_hash_size); | |
800 | memcpy(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size, | |
801 | ke->ke_hash_size); | |
802 | ||
803 | token->len = sizeof(*khdr) + ke->ke_hash_size; | |
804 | rawobj_free(&cksum); | |
805 | return GSS_S_COMPLETE; | |
806 | } | |
807 | ||
808 | static | |
809 | __u32 gss_verify_mic_kerberos(struct gss_ctx *gctx, | |
810 | int msgcnt, | |
811 | rawobj_t *msgs, | |
812 | int iovcnt, | |
813 | lnet_kiov_t *iovs, | |
814 | rawobj_t *token) | |
815 | { | |
816 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
817 | struct krb5_enctype *ke = &enctypes[kctx->kc_enctype]; | |
818 | struct krb5_header *khdr; | |
819 | rawobj_t cksum = RAWOBJ_EMPTY; | |
820 | __u32 major; | |
821 | ||
822 | if (token->len < sizeof(*khdr)) { | |
823 | CERROR("short signature: %u\n", token->len); | |
824 | return GSS_S_DEFECTIVE_TOKEN; | |
825 | } | |
826 | ||
827 | khdr = (struct krb5_header *) token->data; | |
828 | ||
829 | major = verify_krb5_header(kctx, khdr, 0); | |
830 | if (major != GSS_S_COMPLETE) { | |
831 | CERROR("bad krb5 header\n"); | |
832 | return major; | |
833 | } | |
834 | ||
835 | if (token->len < sizeof(*khdr) + ke->ke_hash_size) { | |
836 | CERROR("short signature: %u, require %d\n", | |
837 | token->len, (int) sizeof(*khdr) + ke->ke_hash_size); | |
838 | return GSS_S_FAILURE; | |
839 | } | |
840 | ||
841 | if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc, | |
842 | khdr, msgcnt, msgs, iovcnt, iovs, &cksum)) { | |
843 | CERROR("failed to make checksum\n"); | |
844 | return GSS_S_FAILURE; | |
845 | } | |
846 | ||
847 | LASSERT(cksum.len >= ke->ke_hash_size); | |
848 | if (memcmp(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size, | |
849 | ke->ke_hash_size)) { | |
850 | CERROR("checksum mismatch\n"); | |
851 | rawobj_free(&cksum); | |
852 | return GSS_S_BAD_SIG; | |
853 | } | |
854 | ||
855 | rawobj_free(&cksum); | |
856 | return GSS_S_COMPLETE; | |
857 | } | |
858 | ||
859 | static | |
860 | int add_padding(rawobj_t *msg, int msg_buflen, int blocksize) | |
861 | { | |
862 | int padding; | |
863 | ||
864 | padding = (blocksize - (msg->len & (blocksize - 1))) & | |
865 | (blocksize - 1); | |
866 | if (!padding) | |
867 | return 0; | |
868 | ||
869 | if (msg->len + padding > msg_buflen) { | |
870 | CERROR("bufsize %u too small: datalen %u, padding %u\n", | |
871 | msg_buflen, msg->len, padding); | |
872 | return -EINVAL; | |
873 | } | |
874 | ||
875 | memset(msg->data + msg->len, padding, padding); | |
876 | msg->len += padding; | |
877 | return 0; | |
878 | } | |
879 | ||
880 | static | |
881 | int krb5_encrypt_rawobjs(struct ll_crypto_cipher *tfm, | |
882 | int mode_ecb, | |
883 | int inobj_cnt, | |
884 | rawobj_t *inobjs, | |
885 | rawobj_t *outobj, | |
886 | int enc) | |
887 | { | |
888 | struct blkcipher_desc desc; | |
889 | struct scatterlist src, dst; | |
890 | __u8 local_iv[16] = {0}, *buf; | |
891 | __u32 datalen = 0; | |
892 | int i, rc; | |
d7e09d03 PT |
893 | |
894 | buf = outobj->data; | |
895 | desc.tfm = tfm; | |
896 | desc.info = local_iv; | |
897 | desc.flags = 0; | |
898 | ||
899 | for (i = 0; i < inobj_cnt; i++) { | |
900 | LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len); | |
901 | ||
902 | buf_to_sg(&src, inobjs[i].data, inobjs[i].len); | |
903 | buf_to_sg(&dst, buf, outobj->len - datalen); | |
904 | ||
905 | if (mode_ecb) { | |
906 | if (enc) | |
907 | rc = ll_crypto_blkcipher_encrypt( | |
908 | &desc, &dst, &src, src.length); | |
909 | else | |
910 | rc = ll_crypto_blkcipher_decrypt( | |
911 | &desc, &dst, &src, src.length); | |
912 | } else { | |
913 | if (enc) | |
914 | rc = ll_crypto_blkcipher_encrypt_iv( | |
915 | &desc, &dst, &src, src.length); | |
916 | else | |
917 | rc = ll_crypto_blkcipher_decrypt_iv( | |
918 | &desc, &dst, &src, src.length); | |
919 | } | |
920 | ||
921 | if (rc) { | |
922 | CERROR("encrypt error %d\n", rc); | |
0a3bdb00 | 923 | return rc; |
d7e09d03 PT |
924 | } |
925 | ||
926 | datalen += inobjs[i].len; | |
927 | buf += inobjs[i].len; | |
928 | } | |
929 | ||
930 | outobj->len = datalen; | |
0a3bdb00 | 931 | return 0; |
d7e09d03 PT |
932 | } |
933 | ||
934 | /* | |
935 | * if adj_nob != 0, we adjust desc->bd_nob to the actual cipher text size. | |
936 | */ | |
937 | static | |
938 | int krb5_encrypt_bulk(struct ll_crypto_cipher *tfm, | |
939 | struct krb5_header *khdr, | |
940 | char *confounder, | |
941 | struct ptlrpc_bulk_desc *desc, | |
942 | rawobj_t *cipher, | |
943 | int adj_nob) | |
944 | { | |
945 | struct blkcipher_desc ciph_desc; | |
946 | __u8 local_iv[16] = {0}; | |
947 | struct scatterlist src, dst; | |
948 | int blocksize, i, rc, nob = 0; | |
949 | ||
950 | LASSERT(desc->bd_iov_count); | |
951 | LASSERT(desc->bd_enc_iov); | |
952 | ||
953 | blocksize = ll_crypto_blkcipher_blocksize(tfm); | |
954 | LASSERT(blocksize > 1); | |
955 | LASSERT(cipher->len == blocksize + sizeof(*khdr)); | |
956 | ||
957 | ciph_desc.tfm = tfm; | |
958 | ciph_desc.info = local_iv; | |
959 | ciph_desc.flags = 0; | |
960 | ||
961 | /* encrypt confounder */ | |
962 | buf_to_sg(&src, confounder, blocksize); | |
963 | buf_to_sg(&dst, cipher->data, blocksize); | |
964 | ||
965 | rc = ll_crypto_blkcipher_encrypt_iv(&ciph_desc, &dst, &src, blocksize); | |
966 | if (rc) { | |
967 | CERROR("error to encrypt confounder: %d\n", rc); | |
968 | return rc; | |
969 | } | |
970 | ||
971 | /* encrypt clear pages */ | |
972 | for (i = 0; i < desc->bd_iov_count; i++) { | |
973 | sg_set_page(&src, desc->bd_iov[i].kiov_page, | |
974 | (desc->bd_iov[i].kiov_len + blocksize - 1) & | |
975 | (~(blocksize - 1)), | |
976 | desc->bd_iov[i].kiov_offset); | |
977 | if (adj_nob) | |
978 | nob += src.length; | |
979 | sg_set_page(&dst, desc->bd_enc_iov[i].kiov_page, src.length, | |
980 | src.offset); | |
981 | ||
982 | desc->bd_enc_iov[i].kiov_offset = dst.offset; | |
983 | desc->bd_enc_iov[i].kiov_len = dst.length; | |
984 | ||
985 | rc = ll_crypto_blkcipher_encrypt_iv(&ciph_desc, &dst, &src, | |
986 | src.length); | |
987 | if (rc) { | |
988 | CERROR("error to encrypt page: %d\n", rc); | |
989 | return rc; | |
990 | } | |
991 | } | |
992 | ||
993 | /* encrypt krb5 header */ | |
994 | buf_to_sg(&src, khdr, sizeof(*khdr)); | |
995 | buf_to_sg(&dst, cipher->data + blocksize, sizeof(*khdr)); | |
996 | ||
997 | rc = ll_crypto_blkcipher_encrypt_iv(&ciph_desc, | |
998 | &dst, &src, sizeof(*khdr)); | |
999 | if (rc) { | |
1000 | CERROR("error to encrypt krb5 header: %d\n", rc); | |
1001 | return rc; | |
1002 | } | |
1003 | ||
1004 | if (adj_nob) | |
1005 | desc->bd_nob = nob; | |
1006 | ||
1007 | return 0; | |
1008 | } | |
1009 | ||
1010 | /* | |
1011 | * desc->bd_nob_transferred is the size of cipher text received. | |
1012 | * desc->bd_nob is the target size of plain text supposed to be. | |
1013 | * | |
1014 | * if adj_nob != 0, we adjust each page's kiov_len to the actual | |
1015 | * plain text size. | |
1016 | * - for client read: we don't know data size for each page, so | |
1017 | * bd_iov[]->kiov_len is set to PAGE_SIZE, but actual data received might | |
1018 | * be smaller, so we need to adjust it according to bd_enc_iov[]->kiov_len. | |
1019 | * this means we DO NOT support the situation that server send an odd size | |
1020 | * data in a page which is not the last one. | |
1021 | * - for server write: we knows exactly data size for each page being expected, | |
1022 | * thus kiov_len is accurate already, so we should not adjust it at all. | |
1023 | * and bd_enc_iov[]->kiov_len should be round_up(bd_iov[]->kiov_len) which | |
1024 | * should have been done by prep_bulk(). | |
1025 | */ | |
1026 | static | |
1027 | int krb5_decrypt_bulk(struct ll_crypto_cipher *tfm, | |
1028 | struct krb5_header *khdr, | |
1029 | struct ptlrpc_bulk_desc *desc, | |
1030 | rawobj_t *cipher, | |
1031 | rawobj_t *plain, | |
1032 | int adj_nob) | |
1033 | { | |
1034 | struct blkcipher_desc ciph_desc; | |
1035 | __u8 local_iv[16] = {0}; | |
1036 | struct scatterlist src, dst; | |
1037 | int ct_nob = 0, pt_nob = 0; | |
1038 | int blocksize, i, rc; | |
1039 | ||
1040 | LASSERT(desc->bd_iov_count); | |
1041 | LASSERT(desc->bd_enc_iov); | |
1042 | LASSERT(desc->bd_nob_transferred); | |
1043 | ||
1044 | blocksize = ll_crypto_blkcipher_blocksize(tfm); | |
1045 | LASSERT(blocksize > 1); | |
1046 | LASSERT(cipher->len == blocksize + sizeof(*khdr)); | |
1047 | ||
1048 | ciph_desc.tfm = tfm; | |
1049 | ciph_desc.info = local_iv; | |
1050 | ciph_desc.flags = 0; | |
1051 | ||
1052 | if (desc->bd_nob_transferred % blocksize) { | |
1053 | CERROR("odd transferred nob: %d\n", desc->bd_nob_transferred); | |
1054 | return -EPROTO; | |
1055 | } | |
1056 | ||
1057 | /* decrypt head (confounder) */ | |
1058 | buf_to_sg(&src, cipher->data, blocksize); | |
1059 | buf_to_sg(&dst, plain->data, blocksize); | |
1060 | ||
1061 | rc = ll_crypto_blkcipher_decrypt_iv(&ciph_desc, &dst, &src, blocksize); | |
1062 | if (rc) { | |
1063 | CERROR("error to decrypt confounder: %d\n", rc); | |
1064 | return rc; | |
1065 | } | |
1066 | ||
1067 | for (i = 0; i < desc->bd_iov_count && ct_nob < desc->bd_nob_transferred; | |
1068 | i++) { | |
1069 | if (desc->bd_enc_iov[i].kiov_offset % blocksize != 0 || | |
1070 | desc->bd_enc_iov[i].kiov_len % blocksize != 0) { | |
1071 | CERROR("page %d: odd offset %u len %u, blocksize %d\n", | |
1072 | i, desc->bd_enc_iov[i].kiov_offset, | |
1073 | desc->bd_enc_iov[i].kiov_len, blocksize); | |
1074 | return -EFAULT; | |
1075 | } | |
1076 | ||
1077 | if (adj_nob) { | |
1078 | if (ct_nob + desc->bd_enc_iov[i].kiov_len > | |
1079 | desc->bd_nob_transferred) | |
1080 | desc->bd_enc_iov[i].kiov_len = | |
1081 | desc->bd_nob_transferred - ct_nob; | |
1082 | ||
1083 | desc->bd_iov[i].kiov_len = desc->bd_enc_iov[i].kiov_len; | |
1084 | if (pt_nob + desc->bd_enc_iov[i].kiov_len >desc->bd_nob) | |
1085 | desc->bd_iov[i].kiov_len = desc->bd_nob -pt_nob; | |
1086 | } else { | |
1087 | /* this should be guaranteed by LNET */ | |
1088 | LASSERT(ct_nob + desc->bd_enc_iov[i].kiov_len <= | |
1089 | desc->bd_nob_transferred); | |
1090 | LASSERT(desc->bd_iov[i].kiov_len <= | |
1091 | desc->bd_enc_iov[i].kiov_len); | |
1092 | } | |
1093 | ||
1094 | if (desc->bd_enc_iov[i].kiov_len == 0) | |
1095 | continue; | |
1096 | ||
1097 | sg_set_page(&src, desc->bd_enc_iov[i].kiov_page, | |
1098 | desc->bd_enc_iov[i].kiov_len, | |
1099 | desc->bd_enc_iov[i].kiov_offset); | |
1100 | dst = src; | |
1101 | if (desc->bd_iov[i].kiov_len % blocksize == 0) | |
1102 | sg_assign_page(&dst, desc->bd_iov[i].kiov_page); | |
1103 | ||
1104 | rc = ll_crypto_blkcipher_decrypt_iv(&ciph_desc, &dst, &src, | |
1105 | src.length); | |
1106 | if (rc) { | |
1107 | CERROR("error to decrypt page: %d\n", rc); | |
1108 | return rc; | |
1109 | } | |
1110 | ||
1111 | if (desc->bd_iov[i].kiov_len % blocksize != 0) { | |
1112 | memcpy(page_address(desc->bd_iov[i].kiov_page) + | |
1113 | desc->bd_iov[i].kiov_offset, | |
1114 | page_address(desc->bd_enc_iov[i].kiov_page) + | |
1115 | desc->bd_iov[i].kiov_offset, | |
1116 | desc->bd_iov[i].kiov_len); | |
1117 | } | |
1118 | ||
1119 | ct_nob += desc->bd_enc_iov[i].kiov_len; | |
1120 | pt_nob += desc->bd_iov[i].kiov_len; | |
1121 | } | |
1122 | ||
1123 | if (unlikely(ct_nob != desc->bd_nob_transferred)) { | |
1124 | CERROR("%d cipher text transferred but only %d decrypted\n", | |
1125 | desc->bd_nob_transferred, ct_nob); | |
1126 | return -EFAULT; | |
1127 | } | |
1128 | ||
1129 | if (unlikely(!adj_nob && pt_nob != desc->bd_nob)) { | |
1130 | CERROR("%d plain text expected but only %d received\n", | |
1131 | desc->bd_nob, pt_nob); | |
1132 | return -EFAULT; | |
1133 | } | |
1134 | ||
1135 | /* if needed, clear up the rest unused iovs */ | |
1136 | if (adj_nob) | |
1137 | while (i < desc->bd_iov_count) | |
1138 | desc->bd_iov[i++].kiov_len = 0; | |
1139 | ||
1140 | /* decrypt tail (krb5 header) */ | |
1141 | buf_to_sg(&src, cipher->data + blocksize, sizeof(*khdr)); | |
1142 | buf_to_sg(&dst, cipher->data + blocksize, sizeof(*khdr)); | |
1143 | ||
1144 | rc = ll_crypto_blkcipher_decrypt_iv(&ciph_desc, | |
1145 | &dst, &src, sizeof(*khdr)); | |
1146 | if (rc) { | |
1147 | CERROR("error to decrypt tail: %d\n", rc); | |
1148 | return rc; | |
1149 | } | |
1150 | ||
1151 | if (memcmp(cipher->data + blocksize, khdr, sizeof(*khdr))) { | |
1152 | CERROR("krb5 header doesn't match\n"); | |
1153 | return -EACCES; | |
1154 | } | |
1155 | ||
1156 | return 0; | |
1157 | } | |
1158 | ||
1159 | static | |
1160 | __u32 gss_wrap_kerberos(struct gss_ctx *gctx, | |
1161 | rawobj_t *gsshdr, | |
1162 | rawobj_t *msg, | |
1163 | int msg_buflen, | |
1164 | rawobj_t *token) | |
1165 | { | |
1166 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
1167 | struct krb5_enctype *ke = &enctypes[kctx->kc_enctype]; | |
1168 | struct krb5_header *khdr; | |
1169 | int blocksize; | |
1170 | rawobj_t cksum = RAWOBJ_EMPTY; | |
1171 | rawobj_t data_desc[3], cipher; | |
1172 | __u8 conf[GSS_MAX_CIPHER_BLOCK]; | |
1173 | int rc = 0; | |
1174 | ||
1175 | LASSERT(ke); | |
1176 | LASSERT(ke->ke_conf_size <= GSS_MAX_CIPHER_BLOCK); | |
1177 | LASSERT(kctx->kc_keye.kb_tfm == NULL || | |
1178 | ke->ke_conf_size >= | |
1179 | ll_crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm)); | |
1180 | ||
1181 | /* | |
1182 | * final token format: | |
1183 | * --------------------------------------------------- | |
1184 | * | krb5 header | cipher text | checksum (16 bytes) | | |
1185 | * --------------------------------------------------- | |
1186 | */ | |
1187 | ||
1188 | /* fill krb5 header */ | |
1189 | LASSERT(token->len >= sizeof(*khdr)); | |
1190 | khdr = (struct krb5_header *) token->data; | |
1191 | fill_krb5_header(kctx, khdr, 1); | |
1192 | ||
1193 | /* generate confounder */ | |
1194 | cfs_get_random_bytes(conf, ke->ke_conf_size); | |
1195 | ||
1196 | /* get encryption blocksize. note kc_keye might not associated with | |
1197 | * a tfm, currently only for arcfour-hmac */ | |
1198 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1199 | LASSERT(kctx->kc_keye.kb_tfm == NULL); | |
1200 | blocksize = 1; | |
1201 | } else { | |
1202 | LASSERT(kctx->kc_keye.kb_tfm); | |
1203 | blocksize = ll_crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm); | |
1204 | } | |
1205 | LASSERT(blocksize <= ke->ke_conf_size); | |
1206 | ||
1207 | /* padding the message */ | |
1208 | if (add_padding(msg, msg_buflen, blocksize)) | |
1209 | return GSS_S_FAILURE; | |
1210 | ||
1211 | /* | |
1212 | * clear text layout for checksum: | |
1213 | * ------------------------------------------------------ | |
1214 | * | confounder | gss header | clear msgs | krb5 header | | |
1215 | * ------------------------------------------------------ | |
1216 | */ | |
1217 | data_desc[0].data = conf; | |
1218 | data_desc[0].len = ke->ke_conf_size; | |
1219 | data_desc[1].data = gsshdr->data; | |
1220 | data_desc[1].len = gsshdr->len; | |
1221 | data_desc[2].data = msg->data; | |
1222 | data_desc[2].len = msg->len; | |
1223 | ||
1224 | /* compute checksum */ | |
1225 | if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi, | |
1226 | khdr, 3, data_desc, 0, NULL, &cksum)) | |
1227 | return GSS_S_FAILURE; | |
1228 | LASSERT(cksum.len >= ke->ke_hash_size); | |
1229 | ||
1230 | /* | |
1231 | * clear text layout for encryption: | |
1232 | * ----------------------------------------- | |
1233 | * | confounder | clear msgs | krb5 header | | |
1234 | * ----------------------------------------- | |
1235 | */ | |
1236 | data_desc[0].data = conf; | |
1237 | data_desc[0].len = ke->ke_conf_size; | |
1238 | data_desc[1].data = msg->data; | |
1239 | data_desc[1].len = msg->len; | |
1240 | data_desc[2].data = (__u8 *) khdr; | |
1241 | data_desc[2].len = sizeof(*khdr); | |
1242 | ||
1243 | /* cipher text will be directly inplace */ | |
1244 | cipher.data = (__u8 *) (khdr + 1); | |
1245 | cipher.len = token->len - sizeof(*khdr); | |
1246 | LASSERT(cipher.len >= ke->ke_conf_size + msg->len + sizeof(*khdr)); | |
1247 | ||
1248 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1249 | rawobj_t arc4_keye; | |
1250 | struct ll_crypto_cipher *arc4_tfm; | |
1251 | ||
1252 | if (krb5_make_checksum(ENCTYPE_ARCFOUR_HMAC, &kctx->kc_keyi, | |
1253 | NULL, 1, &cksum, 0, NULL, &arc4_keye)) { | |
1254 | CERROR("failed to obtain arc4 enc key\n"); | |
1255 | GOTO(arc4_out, rc = -EACCES); | |
1256 | } | |
1257 | ||
1258 | arc4_tfm = ll_crypto_alloc_blkcipher("ecb(arc4)", 0, 0); | |
1259 | if (IS_ERR(arc4_tfm)) { | |
1260 | CERROR("failed to alloc tfm arc4 in ECB mode\n"); | |
1261 | GOTO(arc4_out_key, rc = -EACCES); | |
1262 | } | |
1263 | ||
1264 | if (ll_crypto_blkcipher_setkey(arc4_tfm, arc4_keye.data, | |
1265 | arc4_keye.len)) { | |
1266 | CERROR("failed to set arc4 key, len %d\n", | |
1267 | arc4_keye.len); | |
1268 | GOTO(arc4_out_tfm, rc = -EACCES); | |
1269 | } | |
1270 | ||
1271 | rc = krb5_encrypt_rawobjs(arc4_tfm, 1, | |
1272 | 3, data_desc, &cipher, 1); | |
1273 | arc4_out_tfm: | |
1274 | ll_crypto_free_blkcipher(arc4_tfm); | |
1275 | arc4_out_key: | |
1276 | rawobj_free(&arc4_keye); | |
1277 | arc4_out: | |
1278 | do {} while(0); /* just to avoid compile warning */ | |
1279 | } else { | |
1280 | rc = krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 0, | |
1281 | 3, data_desc, &cipher, 1); | |
1282 | } | |
1283 | ||
1284 | if (rc != 0) { | |
1285 | rawobj_free(&cksum); | |
1286 | return GSS_S_FAILURE; | |
1287 | } | |
1288 | ||
1289 | /* fill in checksum */ | |
1290 | LASSERT(token->len >= sizeof(*khdr) + cipher.len + ke->ke_hash_size); | |
1291 | memcpy((char *)(khdr + 1) + cipher.len, | |
1292 | cksum.data + cksum.len - ke->ke_hash_size, | |
1293 | ke->ke_hash_size); | |
1294 | rawobj_free(&cksum); | |
1295 | ||
1296 | /* final token length */ | |
1297 | token->len = sizeof(*khdr) + cipher.len + ke->ke_hash_size; | |
1298 | return GSS_S_COMPLETE; | |
1299 | } | |
1300 | ||
1301 | static | |
1302 | __u32 gss_prep_bulk_kerberos(struct gss_ctx *gctx, | |
1303 | struct ptlrpc_bulk_desc *desc) | |
1304 | { | |
1305 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
1306 | int blocksize, i; | |
1307 | ||
1308 | LASSERT(desc->bd_iov_count); | |
1309 | LASSERT(desc->bd_enc_iov); | |
1310 | LASSERT(kctx->kc_keye.kb_tfm); | |
1311 | ||
1312 | blocksize = ll_crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm); | |
1313 | ||
1314 | for (i = 0; i < desc->bd_iov_count; i++) { | |
1315 | LASSERT(desc->bd_enc_iov[i].kiov_page); | |
1316 | /* | |
1317 | * offset should always start at page boundary of either | |
1318 | * client or server side. | |
1319 | */ | |
1320 | if (desc->bd_iov[i].kiov_offset & blocksize) { | |
1321 | CERROR("odd offset %d in page %d\n", | |
1322 | desc->bd_iov[i].kiov_offset, i); | |
1323 | return GSS_S_FAILURE; | |
1324 | } | |
1325 | ||
1326 | desc->bd_enc_iov[i].kiov_offset = desc->bd_iov[i].kiov_offset; | |
1327 | desc->bd_enc_iov[i].kiov_len = (desc->bd_iov[i].kiov_len + | |
1328 | blocksize - 1) & (~(blocksize - 1)); | |
1329 | } | |
1330 | ||
1331 | return GSS_S_COMPLETE; | |
1332 | } | |
1333 | ||
1334 | static | |
1335 | __u32 gss_wrap_bulk_kerberos(struct gss_ctx *gctx, | |
1336 | struct ptlrpc_bulk_desc *desc, | |
1337 | rawobj_t *token, int adj_nob) | |
1338 | { | |
1339 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
1340 | struct krb5_enctype *ke = &enctypes[kctx->kc_enctype]; | |
1341 | struct krb5_header *khdr; | |
1342 | int blocksize; | |
1343 | rawobj_t cksum = RAWOBJ_EMPTY; | |
1344 | rawobj_t data_desc[1], cipher; | |
1345 | __u8 conf[GSS_MAX_CIPHER_BLOCK]; | |
1346 | int rc = 0; | |
1347 | ||
1348 | LASSERT(ke); | |
1349 | LASSERT(ke->ke_conf_size <= GSS_MAX_CIPHER_BLOCK); | |
1350 | ||
1351 | /* | |
1352 | * final token format: | |
1353 | * -------------------------------------------------- | |
1354 | * | krb5 header | head/tail cipher text | checksum | | |
1355 | * -------------------------------------------------- | |
1356 | */ | |
1357 | ||
1358 | /* fill krb5 header */ | |
1359 | LASSERT(token->len >= sizeof(*khdr)); | |
1360 | khdr = (struct krb5_header *) token->data; | |
1361 | fill_krb5_header(kctx, khdr, 1); | |
1362 | ||
1363 | /* generate confounder */ | |
1364 | cfs_get_random_bytes(conf, ke->ke_conf_size); | |
1365 | ||
1366 | /* get encryption blocksize. note kc_keye might not associated with | |
1367 | * a tfm, currently only for arcfour-hmac */ | |
1368 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1369 | LASSERT(kctx->kc_keye.kb_tfm == NULL); | |
1370 | blocksize = 1; | |
1371 | } else { | |
1372 | LASSERT(kctx->kc_keye.kb_tfm); | |
1373 | blocksize = ll_crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm); | |
1374 | } | |
1375 | ||
1376 | /* | |
1377 | * we assume the size of krb5_header (16 bytes) must be n * blocksize. | |
1378 | * the bulk token size would be exactly (sizeof(krb5_header) + | |
1379 | * blocksize + sizeof(krb5_header) + hashsize) | |
1380 | */ | |
1381 | LASSERT(blocksize <= ke->ke_conf_size); | |
1382 | LASSERT(sizeof(*khdr) >= blocksize && sizeof(*khdr) % blocksize == 0); | |
1383 | LASSERT(token->len >= sizeof(*khdr) + blocksize + sizeof(*khdr) + 16); | |
1384 | ||
1385 | /* | |
1386 | * clear text layout for checksum: | |
1387 | * ------------------------------------------ | |
1388 | * | confounder | clear pages | krb5 header | | |
1389 | * ------------------------------------------ | |
1390 | */ | |
1391 | data_desc[0].data = conf; | |
1392 | data_desc[0].len = ke->ke_conf_size; | |
1393 | ||
1394 | /* compute checksum */ | |
1395 | if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi, | |
1396 | khdr, 1, data_desc, | |
1397 | desc->bd_iov_count, desc->bd_iov, | |
1398 | &cksum)) | |
1399 | return GSS_S_FAILURE; | |
1400 | LASSERT(cksum.len >= ke->ke_hash_size); | |
1401 | ||
1402 | /* | |
1403 | * clear text layout for encryption: | |
1404 | * ------------------------------------------ | |
1405 | * | confounder | clear pages | krb5 header | | |
1406 | * ------------------------------------------ | |
1407 | * | | | | |
1408 | * ---------- (cipher pages) | | |
1409 | * result token: | | | |
1410 | * ------------------------------------------- | |
1411 | * | krb5 header | cipher text | cipher text | | |
1412 | * ------------------------------------------- | |
1413 | */ | |
1414 | data_desc[0].data = conf; | |
1415 | data_desc[0].len = ke->ke_conf_size; | |
1416 | ||
1417 | cipher.data = (__u8 *) (khdr + 1); | |
1418 | cipher.len = blocksize + sizeof(*khdr); | |
1419 | ||
1420 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1421 | LBUG(); | |
1422 | rc = 0; | |
1423 | } else { | |
1424 | rc = krb5_encrypt_bulk(kctx->kc_keye.kb_tfm, khdr, | |
1425 | conf, desc, &cipher, adj_nob); | |
1426 | } | |
1427 | ||
1428 | if (rc != 0) { | |
1429 | rawobj_free(&cksum); | |
1430 | return GSS_S_FAILURE; | |
1431 | } | |
1432 | ||
1433 | /* fill in checksum */ | |
1434 | LASSERT(token->len >= sizeof(*khdr) + cipher.len + ke->ke_hash_size); | |
1435 | memcpy((char *)(khdr + 1) + cipher.len, | |
1436 | cksum.data + cksum.len - ke->ke_hash_size, | |
1437 | ke->ke_hash_size); | |
1438 | rawobj_free(&cksum); | |
1439 | ||
1440 | /* final token length */ | |
1441 | token->len = sizeof(*khdr) + cipher.len + ke->ke_hash_size; | |
1442 | return GSS_S_COMPLETE; | |
1443 | } | |
1444 | ||
1445 | static | |
1446 | __u32 gss_unwrap_kerberos(struct gss_ctx *gctx, | |
1447 | rawobj_t *gsshdr, | |
1448 | rawobj_t *token, | |
1449 | rawobj_t *msg) | |
1450 | { | |
1451 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
1452 | struct krb5_enctype *ke = &enctypes[kctx->kc_enctype]; | |
1453 | struct krb5_header *khdr; | |
1454 | unsigned char *tmpbuf; | |
1455 | int blocksize, bodysize; | |
1456 | rawobj_t cksum = RAWOBJ_EMPTY; | |
1457 | rawobj_t cipher_in, plain_out; | |
1458 | rawobj_t hash_objs[3]; | |
1459 | int rc = 0; | |
1460 | __u32 major; | |
1461 | ||
1462 | LASSERT(ke); | |
1463 | ||
1464 | if (token->len < sizeof(*khdr)) { | |
1465 | CERROR("short signature: %u\n", token->len); | |
1466 | return GSS_S_DEFECTIVE_TOKEN; | |
1467 | } | |
1468 | ||
1469 | khdr = (struct krb5_header *) token->data; | |
1470 | ||
1471 | major = verify_krb5_header(kctx, khdr, 1); | |
1472 | if (major != GSS_S_COMPLETE) { | |
1473 | CERROR("bad krb5 header\n"); | |
1474 | return major; | |
1475 | } | |
1476 | ||
1477 | /* block size */ | |
1478 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1479 | LASSERT(kctx->kc_keye.kb_tfm == NULL); | |
1480 | blocksize = 1; | |
1481 | } else { | |
1482 | LASSERT(kctx->kc_keye.kb_tfm); | |
1483 | blocksize = ll_crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm); | |
1484 | } | |
1485 | ||
1486 | /* expected token layout: | |
1487 | * ---------------------------------------- | |
1488 | * | krb5 header | cipher text | checksum | | |
1489 | * ---------------------------------------- | |
1490 | */ | |
1491 | bodysize = token->len - sizeof(*khdr) - ke->ke_hash_size; | |
1492 | ||
1493 | if (bodysize % blocksize) { | |
1494 | CERROR("odd bodysize %d\n", bodysize); | |
1495 | return GSS_S_DEFECTIVE_TOKEN; | |
1496 | } | |
1497 | ||
1498 | if (bodysize <= ke->ke_conf_size + sizeof(*khdr)) { | |
1499 | CERROR("incomplete token: bodysize %d\n", bodysize); | |
1500 | return GSS_S_DEFECTIVE_TOKEN; | |
1501 | } | |
1502 | ||
1503 | if (msg->len < bodysize - ke->ke_conf_size - sizeof(*khdr)) { | |
1504 | CERROR("buffer too small: %u, require %d\n", | |
1505 | msg->len, bodysize - ke->ke_conf_size); | |
1506 | return GSS_S_FAILURE; | |
1507 | } | |
1508 | ||
1509 | /* decrypting */ | |
1510 | OBD_ALLOC_LARGE(tmpbuf, bodysize); | |
1511 | if (!tmpbuf) | |
1512 | return GSS_S_FAILURE; | |
1513 | ||
1514 | major = GSS_S_FAILURE; | |
1515 | ||
1516 | cipher_in.data = (__u8 *) (khdr + 1); | |
1517 | cipher_in.len = bodysize; | |
1518 | plain_out.data = tmpbuf; | |
1519 | plain_out.len = bodysize; | |
1520 | ||
1521 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1522 | rawobj_t arc4_keye; | |
1523 | struct ll_crypto_cipher *arc4_tfm; | |
1524 | ||
1525 | cksum.data = token->data + token->len - ke->ke_hash_size; | |
1526 | cksum.len = ke->ke_hash_size; | |
1527 | ||
1528 | if (krb5_make_checksum(ENCTYPE_ARCFOUR_HMAC, &kctx->kc_keyi, | |
1529 | NULL, 1, &cksum, 0, NULL, &arc4_keye)) { | |
1530 | CERROR("failed to obtain arc4 enc key\n"); | |
1531 | GOTO(arc4_out, rc = -EACCES); | |
1532 | } | |
1533 | ||
1534 | arc4_tfm = ll_crypto_alloc_blkcipher("ecb(arc4)", 0, 0); | |
1535 | if (IS_ERR(arc4_tfm)) { | |
1536 | CERROR("failed to alloc tfm arc4 in ECB mode\n"); | |
1537 | GOTO(arc4_out_key, rc = -EACCES); | |
1538 | } | |
1539 | ||
1540 | if (ll_crypto_blkcipher_setkey(arc4_tfm, | |
1541 | arc4_keye.data, arc4_keye.len)) { | |
1542 | CERROR("failed to set arc4 key, len %d\n", | |
1543 | arc4_keye.len); | |
1544 | GOTO(arc4_out_tfm, rc = -EACCES); | |
1545 | } | |
1546 | ||
1547 | rc = krb5_encrypt_rawobjs(arc4_tfm, 1, | |
1548 | 1, &cipher_in, &plain_out, 0); | |
1549 | arc4_out_tfm: | |
1550 | ll_crypto_free_blkcipher(arc4_tfm); | |
1551 | arc4_out_key: | |
1552 | rawobj_free(&arc4_keye); | |
1553 | arc4_out: | |
1554 | cksum = RAWOBJ_EMPTY; | |
1555 | } else { | |
1556 | rc = krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 0, | |
1557 | 1, &cipher_in, &plain_out, 0); | |
1558 | } | |
1559 | ||
1560 | if (rc != 0) { | |
1561 | CERROR("error decrypt\n"); | |
1562 | goto out_free; | |
1563 | } | |
1564 | LASSERT(plain_out.len == bodysize); | |
1565 | ||
1566 | /* expected clear text layout: | |
1567 | * ----------------------------------------- | |
1568 | * | confounder | clear msgs | krb5 header | | |
1569 | * ----------------------------------------- | |
1570 | */ | |
1571 | ||
1572 | /* verify krb5 header in token is not modified */ | |
1573 | if (memcmp(khdr, plain_out.data + plain_out.len - sizeof(*khdr), | |
1574 | sizeof(*khdr))) { | |
1575 | CERROR("decrypted krb5 header mismatch\n"); | |
1576 | goto out_free; | |
1577 | } | |
1578 | ||
1579 | /* verify checksum, compose clear text as layout: | |
1580 | * ------------------------------------------------------ | |
1581 | * | confounder | gss header | clear msgs | krb5 header | | |
1582 | * ------------------------------------------------------ | |
1583 | */ | |
1584 | hash_objs[0].len = ke->ke_conf_size; | |
1585 | hash_objs[0].data = plain_out.data; | |
1586 | hash_objs[1].len = gsshdr->len; | |
1587 | hash_objs[1].data = gsshdr->data; | |
1588 | hash_objs[2].len = plain_out.len - ke->ke_conf_size - sizeof(*khdr); | |
1589 | hash_objs[2].data = plain_out.data + ke->ke_conf_size; | |
1590 | if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi, | |
1591 | khdr, 3, hash_objs, 0, NULL, &cksum)) | |
1592 | goto out_free; | |
1593 | ||
1594 | LASSERT(cksum.len >= ke->ke_hash_size); | |
1595 | if (memcmp((char *)(khdr + 1) + bodysize, | |
1596 | cksum.data + cksum.len - ke->ke_hash_size, | |
1597 | ke->ke_hash_size)) { | |
1598 | CERROR("checksum mismatch\n"); | |
1599 | goto out_free; | |
1600 | } | |
1601 | ||
1602 | msg->len = bodysize - ke->ke_conf_size - sizeof(*khdr); | |
1603 | memcpy(msg->data, tmpbuf + ke->ke_conf_size, msg->len); | |
1604 | ||
1605 | major = GSS_S_COMPLETE; | |
1606 | out_free: | |
1607 | OBD_FREE_LARGE(tmpbuf, bodysize); | |
1608 | rawobj_free(&cksum); | |
1609 | return major; | |
1610 | } | |
1611 | ||
1612 | static | |
1613 | __u32 gss_unwrap_bulk_kerberos(struct gss_ctx *gctx, | |
1614 | struct ptlrpc_bulk_desc *desc, | |
1615 | rawobj_t *token, int adj_nob) | |
1616 | { | |
1617 | struct krb5_ctx *kctx = gctx->internal_ctx_id; | |
1618 | struct krb5_enctype *ke = &enctypes[kctx->kc_enctype]; | |
1619 | struct krb5_header *khdr; | |
1620 | int blocksize; | |
1621 | rawobj_t cksum = RAWOBJ_EMPTY; | |
1622 | rawobj_t cipher, plain; | |
1623 | rawobj_t data_desc[1]; | |
1624 | int rc; | |
1625 | __u32 major; | |
1626 | ||
1627 | LASSERT(ke); | |
1628 | ||
1629 | if (token->len < sizeof(*khdr)) { | |
1630 | CERROR("short signature: %u\n", token->len); | |
1631 | return GSS_S_DEFECTIVE_TOKEN; | |
1632 | } | |
1633 | ||
1634 | khdr = (struct krb5_header *) token->data; | |
1635 | ||
1636 | major = verify_krb5_header(kctx, khdr, 1); | |
1637 | if (major != GSS_S_COMPLETE) { | |
1638 | CERROR("bad krb5 header\n"); | |
1639 | return major; | |
1640 | } | |
1641 | ||
1642 | /* block size */ | |
1643 | if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) { | |
1644 | LASSERT(kctx->kc_keye.kb_tfm == NULL); | |
1645 | blocksize = 1; | |
1646 | LBUG(); | |
1647 | } else { | |
1648 | LASSERT(kctx->kc_keye.kb_tfm); | |
1649 | blocksize = ll_crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm); | |
1650 | } | |
1651 | LASSERT(sizeof(*khdr) >= blocksize && sizeof(*khdr) % blocksize == 0); | |
1652 | ||
1653 | /* | |
1654 | * token format is expected as: | |
1655 | * ----------------------------------------------- | |
1656 | * | krb5 header | head/tail cipher text | cksum | | |
1657 | * ----------------------------------------------- | |
1658 | */ | |
1659 | if (token->len < sizeof(*khdr) + blocksize + sizeof(*khdr) + | |
1660 | ke->ke_hash_size) { | |
1661 | CERROR("short token size: %u\n", token->len); | |
1662 | return GSS_S_DEFECTIVE_TOKEN; | |
1663 | } | |
1664 | ||
1665 | cipher.data = (__u8 *) (khdr + 1); | |
1666 | cipher.len = blocksize + sizeof(*khdr); | |
1667 | plain.data = cipher.data; | |
1668 | plain.len = cipher.len; | |
1669 | ||
1670 | rc = krb5_decrypt_bulk(kctx->kc_keye.kb_tfm, khdr, | |
1671 | desc, &cipher, &plain, adj_nob); | |
1672 | if (rc) | |
1673 | return GSS_S_DEFECTIVE_TOKEN; | |
1674 | ||
1675 | /* | |
1676 | * verify checksum, compose clear text as layout: | |
1677 | * ------------------------------------------ | |
1678 | * | confounder | clear pages | krb5 header | | |
1679 | * ------------------------------------------ | |
1680 | */ | |
1681 | data_desc[0].data = plain.data; | |
1682 | data_desc[0].len = blocksize; | |
1683 | ||
1684 | if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi, | |
1685 | khdr, 1, data_desc, | |
1686 | desc->bd_iov_count, desc->bd_iov, | |
1687 | &cksum)) | |
1688 | return GSS_S_FAILURE; | |
1689 | LASSERT(cksum.len >= ke->ke_hash_size); | |
1690 | ||
1691 | if (memcmp(plain.data + blocksize + sizeof(*khdr), | |
1692 | cksum.data + cksum.len - ke->ke_hash_size, | |
1693 | ke->ke_hash_size)) { | |
1694 | CERROR("checksum mismatch\n"); | |
1695 | rawobj_free(&cksum); | |
1696 | return GSS_S_BAD_SIG; | |
1697 | } | |
1698 | ||
1699 | rawobj_free(&cksum); | |
1700 | return GSS_S_COMPLETE; | |
1701 | } | |
1702 | ||
1703 | int gss_display_kerberos(struct gss_ctx *ctx, | |
1704 | char *buf, | |
1705 | int bufsize) | |
1706 | { | |
1707 | struct krb5_ctx *kctx = ctx->internal_ctx_id; | |
1708 | int written; | |
1709 | ||
1710 | written = snprintf(buf, bufsize, "krb5 (%s)", | |
1711 | enctype2str(kctx->kc_enctype)); | |
1712 | return written; | |
1713 | } | |
1714 | ||
1715 | static struct gss_api_ops gss_kerberos_ops = { | |
1716 | .gss_import_sec_context = gss_import_sec_context_kerberos, | |
1717 | .gss_copy_reverse_context = gss_copy_reverse_context_kerberos, | |
1718 | .gss_inquire_context = gss_inquire_context_kerberos, | |
1719 | .gss_get_mic = gss_get_mic_kerberos, | |
1720 | .gss_verify_mic = gss_verify_mic_kerberos, | |
1721 | .gss_wrap = gss_wrap_kerberos, | |
1722 | .gss_unwrap = gss_unwrap_kerberos, | |
1723 | .gss_prep_bulk = gss_prep_bulk_kerberos, | |
1724 | .gss_wrap_bulk = gss_wrap_bulk_kerberos, | |
1725 | .gss_unwrap_bulk = gss_unwrap_bulk_kerberos, | |
1726 | .gss_delete_sec_context = gss_delete_sec_context_kerberos, | |
1727 | .gss_display = gss_display_kerberos, | |
1728 | }; | |
1729 | ||
1730 | static struct subflavor_desc gss_kerberos_sfs[] = { | |
1731 | { | |
1732 | .sf_subflavor = SPTLRPC_SUBFLVR_KRB5N, | |
1733 | .sf_qop = 0, | |
1734 | .sf_service = SPTLRPC_SVC_NULL, | |
1735 | .sf_name = "krb5n" | |
1736 | }, | |
1737 | { | |
1738 | .sf_subflavor = SPTLRPC_SUBFLVR_KRB5A, | |
1739 | .sf_qop = 0, | |
1740 | .sf_service = SPTLRPC_SVC_AUTH, | |
1741 | .sf_name = "krb5a" | |
1742 | }, | |
1743 | { | |
1744 | .sf_subflavor = SPTLRPC_SUBFLVR_KRB5I, | |
1745 | .sf_qop = 0, | |
1746 | .sf_service = SPTLRPC_SVC_INTG, | |
1747 | .sf_name = "krb5i" | |
1748 | }, | |
1749 | { | |
1750 | .sf_subflavor = SPTLRPC_SUBFLVR_KRB5P, | |
1751 | .sf_qop = 0, | |
1752 | .sf_service = SPTLRPC_SVC_PRIV, | |
1753 | .sf_name = "krb5p" | |
1754 | }, | |
1755 | }; | |
1756 | ||
1757 | /* | |
1758 | * currently we leave module owner NULL | |
1759 | */ | |
1760 | static struct gss_api_mech gss_kerberos_mech = { | |
1761 | .gm_owner = NULL, /*THIS_MODULE, */ | |
1762 | .gm_name = "krb5", | |
1763 | .gm_oid = (rawobj_t) | |
1764 | {9, "\052\206\110\206\367\022\001\002\002"}, | |
1765 | .gm_ops = &gss_kerberos_ops, | |
1766 | .gm_sf_num = 4, | |
1767 | .gm_sfs = gss_kerberos_sfs, | |
1768 | }; | |
1769 | ||
1770 | int __init init_kerberos_module(void) | |
1771 | { | |
1772 | int status; | |
1773 | ||
1774 | spin_lock_init(&krb5_seq_lock); | |
1775 | ||
1776 | status = lgss_mech_register(&gss_kerberos_mech); | |
1777 | if (status) | |
1778 | CERROR("Failed to register kerberos gss mechanism!\n"); | |
1779 | return status; | |
1780 | } | |
1781 | ||
1782 | void __exit cleanup_kerberos_module(void) | |
1783 | { | |
1784 | lgss_mech_unregister(&gss_kerberos_mech); | |
1785 | } |