diff options
author | Kevin Coffman <kwc@citi.umich.edu> | 2010-03-17 13:03:00 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-14 15:09:19 -0400 |
commit | 934a95aa1c9c6ad77838800b79c306e982437605 (patch) | |
tree | 0f7000ffce214a156737fddc127fb0af238dfcff /net/sunrpc/auth_gss/gss_krb5_crypto.c | |
parent | de9c17eb4a912c9028f7b470eb80815144883b26 (diff) |
gss_krb5: add remaining pieces to enable AES encryption support
Add the remaining pieces to enable support for Kerberos AES
encryption types.
Signed-off-by: Kevin Coffman <kwc@citi.umich.edu>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc/auth_gss/gss_krb5_crypto.c')
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_crypto.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index ca52ac28a537..967484a914f3 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/crypto.h> | 41 | #include <linux/crypto.h> |
42 | #include <linux/highmem.h> | 42 | #include <linux/highmem.h> |
43 | #include <linux/pagemap.h> | 43 | #include <linux/pagemap.h> |
44 | #include <linux/random.h> | ||
44 | #include <linux/sunrpc/gss_krb5.h> | 45 | #include <linux/sunrpc/gss_krb5.h> |
45 | #include <linux/sunrpc/xdr.h> | 46 | #include <linux/sunrpc/xdr.h> |
46 | 47 | ||
@@ -478,3 +479,250 @@ xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen) | |||
478 | 479 | ||
479 | return 0; | 480 | return 0; |
480 | } | 481 | } |
482 | |||
483 | static u32 | ||
484 | gss_krb5_cts_crypt(struct crypto_blkcipher *cipher, struct xdr_buf *buf, | ||
485 | u32 offset, u8 *iv, struct page **pages, int encrypt) | ||
486 | { | ||
487 | u32 ret; | ||
488 | struct scatterlist sg[1]; | ||
489 | struct blkcipher_desc desc = { .tfm = cipher, .info = iv }; | ||
490 | u8 data[crypto_blkcipher_blocksize(cipher) * 2]; | ||
491 | struct page **save_pages; | ||
492 | u32 len = buf->len - offset; | ||
493 | |||
494 | BUG_ON(len > crypto_blkcipher_blocksize(cipher) * 2); | ||
495 | |||
496 | /* | ||
497 | * For encryption, we want to read from the cleartext | ||
498 | * page cache pages, and write the encrypted data to | ||
499 | * the supplied xdr_buf pages. | ||
500 | */ | ||
501 | save_pages = buf->pages; | ||
502 | if (encrypt) | ||
503 | buf->pages = pages; | ||
504 | |||
505 | ret = read_bytes_from_xdr_buf(buf, offset, data, len); | ||
506 | buf->pages = save_pages; | ||
507 | if (ret) | ||
508 | goto out; | ||
509 | |||
510 | sg_init_one(sg, data, len); | ||
511 | |||
512 | if (encrypt) | ||
513 | ret = crypto_blkcipher_encrypt_iv(&desc, sg, sg, len); | ||
514 | else | ||
515 | ret = crypto_blkcipher_decrypt_iv(&desc, sg, sg, len); | ||
516 | |||
517 | if (ret) | ||
518 | goto out; | ||
519 | |||
520 | ret = write_bytes_to_xdr_buf(buf, offset, data, len); | ||
521 | |||
522 | out: | ||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | u32 | ||
527 | gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset, | ||
528 | struct xdr_buf *buf, int ec, struct page **pages) | ||
529 | { | ||
530 | u32 err; | ||
531 | struct xdr_netobj hmac; | ||
532 | u8 *cksumkey; | ||
533 | u8 *ecptr; | ||
534 | struct crypto_blkcipher *cipher, *aux_cipher; | ||
535 | int blocksize; | ||
536 | struct page **save_pages; | ||
537 | int nblocks, nbytes; | ||
538 | struct encryptor_desc desc; | ||
539 | u32 cbcbytes; | ||
540 | |||
541 | if (kctx->initiate) { | ||
542 | cipher = kctx->initiator_enc; | ||
543 | aux_cipher = kctx->initiator_enc_aux; | ||
544 | cksumkey = kctx->initiator_integ; | ||
545 | } else { | ||
546 | cipher = kctx->acceptor_enc; | ||
547 | aux_cipher = kctx->acceptor_enc_aux; | ||
548 | cksumkey = kctx->acceptor_integ; | ||
549 | } | ||
550 | blocksize = crypto_blkcipher_blocksize(cipher); | ||
551 | |||
552 | /* hide the gss token header and insert the confounder */ | ||
553 | offset += GSS_KRB5_TOK_HDR_LEN; | ||
554 | if (xdr_extend_head(buf, offset, blocksize)) | ||
555 | return GSS_S_FAILURE; | ||
556 | gss_krb5_make_confounder(buf->head[0].iov_base + offset, blocksize); | ||
557 | offset -= GSS_KRB5_TOK_HDR_LEN; | ||
558 | |||
559 | if (buf->tail[0].iov_base != NULL) { | ||
560 | ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len; | ||
561 | } else { | ||
562 | buf->tail[0].iov_base = buf->head[0].iov_base | ||
563 | + buf->head[0].iov_len; | ||
564 | buf->tail[0].iov_len = 0; | ||
565 | ecptr = buf->tail[0].iov_base; | ||
566 | } | ||
567 | |||
568 | memset(ecptr, 'X', ec); | ||
569 | buf->tail[0].iov_len += ec; | ||
570 | buf->len += ec; | ||
571 | |||
572 | /* copy plaintext gss token header after filler (if any) */ | ||
573 | memcpy(ecptr + ec, buf->head[0].iov_base + offset, | ||
574 | GSS_KRB5_TOK_HDR_LEN); | ||
575 | buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN; | ||
576 | buf->len += GSS_KRB5_TOK_HDR_LEN; | ||
577 | |||
578 | /* Do the HMAC */ | ||
579 | hmac.len = GSS_KRB5_MAX_CKSUM_LEN; | ||
580 | hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len; | ||
581 | |||
582 | /* | ||
583 | * When we are called, pages points to the real page cache | ||
584 | * data -- which we can't go and encrypt! buf->pages points | ||
585 | * to scratch pages which we are going to send off to the | ||
586 | * client/server. Swap in the plaintext pages to calculate | ||
587 | * the hmac. | ||
588 | */ | ||
589 | save_pages = buf->pages; | ||
590 | buf->pages = pages; | ||
591 | |||
592 | err = make_checksum_v2(kctx, NULL, 0, buf, | ||
593 | offset + GSS_KRB5_TOK_HDR_LEN, cksumkey, &hmac); | ||
594 | buf->pages = save_pages; | ||
595 | if (err) | ||
596 | return GSS_S_FAILURE; | ||
597 | |||
598 | nbytes = buf->len - offset - GSS_KRB5_TOK_HDR_LEN; | ||
599 | nblocks = (nbytes + blocksize - 1) / blocksize; | ||
600 | cbcbytes = 0; | ||
601 | if (nblocks > 2) | ||
602 | cbcbytes = (nblocks - 2) * blocksize; | ||
603 | |||
604 | memset(desc.iv, 0, sizeof(desc.iv)); | ||
605 | |||
606 | if (cbcbytes) { | ||
607 | desc.pos = offset + GSS_KRB5_TOK_HDR_LEN; | ||
608 | desc.fragno = 0; | ||
609 | desc.fraglen = 0; | ||
610 | desc.pages = pages; | ||
611 | desc.outbuf = buf; | ||
612 | desc.desc.info = desc.iv; | ||
613 | desc.desc.flags = 0; | ||
614 | desc.desc.tfm = aux_cipher; | ||
615 | |||
616 | sg_init_table(desc.infrags, 4); | ||
617 | sg_init_table(desc.outfrags, 4); | ||
618 | |||
619 | err = xdr_process_buf(buf, offset + GSS_KRB5_TOK_HDR_LEN, | ||
620 | cbcbytes, encryptor, &desc); | ||
621 | if (err) | ||
622 | goto out_err; | ||
623 | } | ||
624 | |||
625 | /* Make sure IV carries forward from any CBC results. */ | ||
626 | err = gss_krb5_cts_crypt(cipher, buf, | ||
627 | offset + GSS_KRB5_TOK_HDR_LEN + cbcbytes, | ||
628 | desc.iv, pages, 1); | ||
629 | if (err) { | ||
630 | err = GSS_S_FAILURE; | ||
631 | goto out_err; | ||
632 | } | ||
633 | |||
634 | /* Now update buf to account for HMAC */ | ||
635 | buf->tail[0].iov_len += kctx->gk5e->cksumlength; | ||
636 | buf->len += kctx->gk5e->cksumlength; | ||
637 | |||
638 | out_err: | ||
639 | if (err) | ||
640 | err = GSS_S_FAILURE; | ||
641 | return err; | ||
642 | } | ||
643 | |||
644 | u32 | ||
645 | gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, | ||
646 | u32 *headskip, u32 *tailskip) | ||
647 | { | ||
648 | struct xdr_buf subbuf; | ||
649 | u32 ret = 0; | ||
650 | u8 *cksum_key; | ||
651 | struct crypto_blkcipher *cipher, *aux_cipher; | ||
652 | struct xdr_netobj our_hmac_obj; | ||
653 | u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN]; | ||
654 | u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN]; | ||
655 | int nblocks, blocksize, cbcbytes; | ||
656 | struct decryptor_desc desc; | ||
657 | |||
658 | if (kctx->initiate) { | ||
659 | cipher = kctx->acceptor_enc; | ||
660 | aux_cipher = kctx->acceptor_enc_aux; | ||
661 | cksum_key = kctx->acceptor_integ; | ||
662 | } else { | ||
663 | cipher = kctx->initiator_enc; | ||
664 | aux_cipher = kctx->initiator_enc_aux; | ||
665 | cksum_key = kctx->initiator_integ; | ||
666 | } | ||
667 | blocksize = crypto_blkcipher_blocksize(cipher); | ||
668 | |||
669 | |||
670 | /* create a segment skipping the header and leaving out the checksum */ | ||
671 | xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN, | ||
672 | (buf->len - offset - GSS_KRB5_TOK_HDR_LEN - | ||
673 | kctx->gk5e->cksumlength)); | ||
674 | |||
675 | nblocks = (subbuf.len + blocksize - 1) / blocksize; | ||
676 | |||
677 | cbcbytes = 0; | ||
678 | if (nblocks > 2) | ||
679 | cbcbytes = (nblocks - 2) * blocksize; | ||
680 | |||
681 | memset(desc.iv, 0, sizeof(desc.iv)); | ||
682 | |||
683 | if (cbcbytes) { | ||
684 | desc.fragno = 0; | ||
685 | desc.fraglen = 0; | ||
686 | desc.desc.info = desc.iv; | ||
687 | desc.desc.flags = 0; | ||
688 | desc.desc.tfm = aux_cipher; | ||
689 | |||
690 | sg_init_table(desc.frags, 4); | ||
691 | |||
692 | ret = xdr_process_buf(&subbuf, 0, cbcbytes, decryptor, &desc); | ||
693 | if (ret) | ||
694 | goto out_err; | ||
695 | } | ||
696 | |||
697 | /* Make sure IV carries forward from any CBC results. */ | ||
698 | ret = gss_krb5_cts_crypt(cipher, &subbuf, cbcbytes, desc.iv, NULL, 0); | ||
699 | if (ret) | ||
700 | goto out_err; | ||
701 | |||
702 | |||
703 | /* Calculate our hmac over the plaintext data */ | ||
704 | our_hmac_obj.len = sizeof(our_hmac); | ||
705 | our_hmac_obj.data = our_hmac; | ||
706 | |||
707 | ret = make_checksum_v2(kctx, NULL, 0, &subbuf, 0, | ||
708 | cksum_key, &our_hmac_obj); | ||
709 | if (ret) | ||
710 | goto out_err; | ||
711 | |||
712 | /* Get the packet's hmac value */ | ||
713 | ret = read_bytes_from_xdr_buf(buf, buf->len - kctx->gk5e->cksumlength, | ||
714 | pkt_hmac, kctx->gk5e->cksumlength); | ||
715 | if (ret) | ||
716 | goto out_err; | ||
717 | |||
718 | if (memcmp(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) { | ||
719 | ret = GSS_S_BAD_SIG; | ||
720 | goto out_err; | ||
721 | } | ||
722 | *headskip = crypto_blkcipher_blocksize(cipher); | ||
723 | *tailskip = kctx->gk5e->cksumlength; | ||
724 | out_err: | ||
725 | if (ret && ret != GSS_S_BAD_SIG) | ||
726 | ret = GSS_S_FAILURE; | ||
727 | return ret; | ||
728 | } | ||