Skip to content

Commit

Permalink
gss_krb5: add remaining pieces to enable AES encryption support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Kevin Coffman authored and Trond Myklebust committed May 14, 2010
1 parent de9c17e commit 934a95a
Show file tree
Hide file tree
Showing 5 changed files with 378 additions and 12 deletions.
20 changes: 20 additions & 0 deletions include/linux/sunrpc/gss_krb5.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ struct krb5_ctx {
struct crypto_blkcipher *seq;
struct crypto_blkcipher *acceptor_enc;
struct crypto_blkcipher *initiator_enc;
struct crypto_blkcipher *acceptor_enc_aux;
struct crypto_blkcipher *initiator_enc_aux;
u8 cksum[GSS_KRB5_MAX_KEYLEN];
s32 endtime;
u32 seq_send;
Expand Down Expand Up @@ -294,3 +296,21 @@ u32
gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
struct xdr_netobj *randombits,
struct xdr_netobj *key);

u32
gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
struct xdr_netobj *randombits,
struct xdr_netobj *key);

u32
gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, int ec,
struct page **pages);

u32
gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, u32 *plainoffset,
u32 *plainlen);

void
gss_krb5_make_confounder(char *p, u32 conflen);
248 changes: 248 additions & 0 deletions net/sunrpc/auth_gss/gss_krb5_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <linux/crypto.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/random.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>

Expand Down Expand Up @@ -478,3 +479,250 @@ xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)

return 0;
}

static u32
gss_krb5_cts_crypt(struct crypto_blkcipher *cipher, struct xdr_buf *buf,
u32 offset, u8 *iv, struct page **pages, int encrypt)
{
u32 ret;
struct scatterlist sg[1];
struct blkcipher_desc desc = { .tfm = cipher, .info = iv };
u8 data[crypto_blkcipher_blocksize(cipher) * 2];
struct page **save_pages;
u32 len = buf->len - offset;

BUG_ON(len > crypto_blkcipher_blocksize(cipher) * 2);

/*
* For encryption, we want to read from the cleartext
* page cache pages, and write the encrypted data to
* the supplied xdr_buf pages.
*/
save_pages = buf->pages;
if (encrypt)
buf->pages = pages;

ret = read_bytes_from_xdr_buf(buf, offset, data, len);
buf->pages = save_pages;
if (ret)
goto out;

sg_init_one(sg, data, len);

if (encrypt)
ret = crypto_blkcipher_encrypt_iv(&desc, sg, sg, len);
else
ret = crypto_blkcipher_decrypt_iv(&desc, sg, sg, len);

if (ret)
goto out;

ret = write_bytes_to_xdr_buf(buf, offset, data, len);

out:
return ret;
}

u32
gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, int ec, struct page **pages)
{
u32 err;
struct xdr_netobj hmac;
u8 *cksumkey;
u8 *ecptr;
struct crypto_blkcipher *cipher, *aux_cipher;
int blocksize;
struct page **save_pages;
int nblocks, nbytes;
struct encryptor_desc desc;
u32 cbcbytes;

if (kctx->initiate) {
cipher = kctx->initiator_enc;
aux_cipher = kctx->initiator_enc_aux;
cksumkey = kctx->initiator_integ;
} else {
cipher = kctx->acceptor_enc;
aux_cipher = kctx->acceptor_enc_aux;
cksumkey = kctx->acceptor_integ;
}
blocksize = crypto_blkcipher_blocksize(cipher);

/* hide the gss token header and insert the confounder */
offset += GSS_KRB5_TOK_HDR_LEN;
if (xdr_extend_head(buf, offset, blocksize))
return GSS_S_FAILURE;
gss_krb5_make_confounder(buf->head[0].iov_base + offset, blocksize);
offset -= GSS_KRB5_TOK_HDR_LEN;

if (buf->tail[0].iov_base != NULL) {
ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
} else {
buf->tail[0].iov_base = buf->head[0].iov_base
+ buf->head[0].iov_len;
buf->tail[0].iov_len = 0;
ecptr = buf->tail[0].iov_base;
}

memset(ecptr, 'X', ec);
buf->tail[0].iov_len += ec;
buf->len += ec;

/* copy plaintext gss token header after filler (if any) */
memcpy(ecptr + ec, buf->head[0].iov_base + offset,
GSS_KRB5_TOK_HDR_LEN);
buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
buf->len += GSS_KRB5_TOK_HDR_LEN;

/* Do the HMAC */
hmac.len = GSS_KRB5_MAX_CKSUM_LEN;
hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len;

/*
* When we are called, pages points to the real page cache
* data -- which we can't go and encrypt! buf->pages points
* to scratch pages which we are going to send off to the
* client/server. Swap in the plaintext pages to calculate
* the hmac.
*/
save_pages = buf->pages;
buf->pages = pages;

err = make_checksum_v2(kctx, NULL, 0, buf,
offset + GSS_KRB5_TOK_HDR_LEN, cksumkey, &hmac);
buf->pages = save_pages;
if (err)
return GSS_S_FAILURE;

nbytes = buf->len - offset - GSS_KRB5_TOK_HDR_LEN;
nblocks = (nbytes + blocksize - 1) / blocksize;
cbcbytes = 0;
if (nblocks > 2)
cbcbytes = (nblocks - 2) * blocksize;

memset(desc.iv, 0, sizeof(desc.iv));

if (cbcbytes) {
desc.pos = offset + GSS_KRB5_TOK_HDR_LEN;
desc.fragno = 0;
desc.fraglen = 0;
desc.pages = pages;
desc.outbuf = buf;
desc.desc.info = desc.iv;
desc.desc.flags = 0;
desc.desc.tfm = aux_cipher;

sg_init_table(desc.infrags, 4);
sg_init_table(desc.outfrags, 4);

err = xdr_process_buf(buf, offset + GSS_KRB5_TOK_HDR_LEN,
cbcbytes, encryptor, &desc);
if (err)
goto out_err;
}

/* Make sure IV carries forward from any CBC results. */
err = gss_krb5_cts_crypt(cipher, buf,
offset + GSS_KRB5_TOK_HDR_LEN + cbcbytes,
desc.iv, pages, 1);
if (err) {
err = GSS_S_FAILURE;
goto out_err;
}

/* Now update buf to account for HMAC */
buf->tail[0].iov_len += kctx->gk5e->cksumlength;
buf->len += kctx->gk5e->cksumlength;

out_err:
if (err)
err = GSS_S_FAILURE;
return err;
}

u32
gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf,
u32 *headskip, u32 *tailskip)
{
struct xdr_buf subbuf;
u32 ret = 0;
u8 *cksum_key;
struct crypto_blkcipher *cipher, *aux_cipher;
struct xdr_netobj our_hmac_obj;
u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN];
u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN];
int nblocks, blocksize, cbcbytes;
struct decryptor_desc desc;

if (kctx->initiate) {
cipher = kctx->acceptor_enc;
aux_cipher = kctx->acceptor_enc_aux;
cksum_key = kctx->acceptor_integ;
} else {
cipher = kctx->initiator_enc;
aux_cipher = kctx->initiator_enc_aux;
cksum_key = kctx->initiator_integ;
}
blocksize = crypto_blkcipher_blocksize(cipher);


/* create a segment skipping the header and leaving out the checksum */
xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
(buf->len - offset - GSS_KRB5_TOK_HDR_LEN -
kctx->gk5e->cksumlength));

nblocks = (subbuf.len + blocksize - 1) / blocksize;

cbcbytes = 0;
if (nblocks > 2)
cbcbytes = (nblocks - 2) * blocksize;

memset(desc.iv, 0, sizeof(desc.iv));

if (cbcbytes) {
desc.fragno = 0;
desc.fraglen = 0;
desc.desc.info = desc.iv;
desc.desc.flags = 0;
desc.desc.tfm = aux_cipher;

sg_init_table(desc.frags, 4);

ret = xdr_process_buf(&subbuf, 0, cbcbytes, decryptor, &desc);
if (ret)
goto out_err;
}

/* Make sure IV carries forward from any CBC results. */
ret = gss_krb5_cts_crypt(cipher, &subbuf, cbcbytes, desc.iv, NULL, 0);
if (ret)
goto out_err;


/* Calculate our hmac over the plaintext data */
our_hmac_obj.len = sizeof(our_hmac);
our_hmac_obj.data = our_hmac;

ret = make_checksum_v2(kctx, NULL, 0, &subbuf, 0,
cksum_key, &our_hmac_obj);
if (ret)
goto out_err;

/* Get the packet's hmac value */
ret = read_bytes_from_xdr_buf(buf, buf->len - kctx->gk5e->cksumlength,
pkt_hmac, kctx->gk5e->cksumlength);
if (ret)
goto out_err;

if (memcmp(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
ret = GSS_S_BAD_SIG;
goto out_err;
}
*headskip = crypto_blkcipher_blocksize(cipher);
*tailskip = kctx->gk5e->cksumlength;
out_err:
if (ret && ret != GSS_S_BAD_SIG)
ret = GSS_S_FAILURE;
return ret;
}
30 changes: 30 additions & 0 deletions net/sunrpc/auth_gss/gss_krb5_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,33 @@ u32 gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
err_out:
return ret;
}

/*
* This is the aes key derivation postprocess function
*/
u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
struct xdr_netobj *randombits,
struct xdr_netobj *key)
{
u32 ret = EINVAL;

if (key->len != 16 && key->len != 32) {
dprintk("%s: key->len is %d\n", __func__, key->len);
goto err_out;
}
if (randombits->len != 16 && randombits->len != 32) {
dprintk("%s: randombits->len is %d\n",
__func__, randombits->len);
goto err_out;
}
if (randombits->len != key->len) {
dprintk("%s: randombits->len is %d, key->len is %d\n",
__func__, randombits->len, key->len);
goto err_out;
}
memcpy(key->data, randombits->data, key->len);
ret = 0;
err_out:
return ret;
}

Loading

0 comments on commit 934a95a

Please sign in to comment.