-/*
- * Compute a checksum over an mbuf chain.
- * Start building an MD5 digest at the given offset and keep
- * going until the end of data in the current mbuf is reached.
- * Then convert the 16 byte MD5 digest to an 8 byte DES CBC
- * checksum.
- */
-static void
-nfs_gss_cksum_mchain(
- gss_key_info *ki,
- mbuf_t mhead,
- u_char *alg,
- int offset,
- int len,
- u_char *digest)
-{
- mbuf_t mb;
- u_char *ptr;
- int left, bytes;
- GSS_DIGEST_CTX context;
-
- gss_digest_Init(&context, ki);
-
- /*
- * Logically prepend the first 8 bytes of the algorithm
- * field as required by RFC 1964, section 1.2.1.1
- */
- gss_digest_Update(&context, alg, KRB5_SZ_ALG);
-
- /*
- * Move down the mbuf chain until we reach the given
- * byte offset, then start MD5 on the mbuf data until
- * we've done len bytes.
- */
-
- for (mb = mhead; mb && len > 0; mb = mbuf_next(mb)) {
- ptr = mbuf_data(mb);
- left = mbuf_len(mb);
- if (offset >= left) {
- /* Offset not yet reached */
- offset -= left;
- continue;
- }
- /* At or beyond offset - checksum data */
- ptr += offset;
- left -= offset;
- offset = 0;
-
- bytes = left < len ? left : len;
- if (bytes > 0)
- gss_digest_Update(&context, ptr, bytes);
- len -= bytes;
- }
-
- gss_digest_Final(&context, digest);
-}
-
-/*
- * Compute a checksum over an NFS mbuf chain.
- * Start building an MD5 digest at the given offset and keep
- * going until the end of data in the current mbuf is reached.
- * Then convert the 16 byte MD5 digest to an 8 byte DES CBC
- * checksum.
- */
-static void
-nfs_gss_cksum_chain(
- gss_key_info *ki,
- struct nfsm_chain *nmc,
- u_char *alg,
- int offset,
- int len,
- u_char *cksum)
-{
- /*
- * If the length parameter is zero, then we need
- * to use the length from the offset to the current
- * encode/decode offset.
- */
- if (len == 0)
- len = nfsm_chain_offset(nmc) - offset;
-
- return (nfs_gss_cksum_mchain(ki, nmc->nmc_mhead, alg, offset, len, cksum));
-}
-
-/*
- * Compute a checksum of the sequence number (or sequence window)
- * of an RPCSEC_GSS reply.
- */
-static void
-nfs_gss_cksum_rep(gss_key_info *ki, uint32_t seqnum, u_char *cksum)
-{
- GSS_DIGEST_CTX context;
- uint32_t val = htonl(seqnum);
-
- gss_digest_Init(&context, ki);
-
- /*
- * Logically prepend the first 8 bytes of the MIC
- * token as required by RFC 1964, section 1.2.1.1
- */
- gss_digest_Update(&context, ALG_MIC(ki), KRB5_SZ_ALG);
-
- /*
- * Compute the digest of the seqnum in network order
- */
- gss_digest_Update(&context, &val, 4);
- gss_digest_Final(&context, cksum);
-}
-
-/*
- * Encrypt or decrypt data in an mbuf chain with des-cbc.
- */
-static void
-nfs_gss_encrypt_mchain(
- gss_key_info *ki,
- mbuf_t mhead,
- int offset,
- int len,
- int encrypt)
-{
- mbuf_t mb, mbn;
- u_char *ptr, *nptr;
- u_char tmp[8], ivec[8];
- int left, left8, remain;
-
-
- bzero(ivec, 8);
-
- /*
- * Move down the mbuf chain until we reach the given
- * byte offset, then start encrypting the mbuf data until
- * we've done len bytes.
- */
-
- for (mb = mhead; mb && len > 0; mb = mbn) {
- mbn = mbuf_next(mb);
- ptr = mbuf_data(mb);
- left = mbuf_len(mb);
- if (offset >= left) {
- /* Offset not yet reached */
- offset -= left;
- continue;
- }
- /* At or beyond offset - encrypt data */
- ptr += offset;
- left -= offset;
- offset = 0;
-
- /*
- * DES or DES3 CBC has to encrypt 8 bytes at a time.
- * If the number of bytes to be encrypted in this
- * mbuf isn't some multiple of 8 bytes, encrypt all
- * the 8 byte blocks, then combine the remaining
- * bytes with enough from the next mbuf to make up
- * an 8 byte block and encrypt that block separately,
- * i.e. that block is split across two mbufs.
- */
- remain = left % 8;
- left8 = left - remain;
- left = left8 < len ? left8 : len;
- if (left > 0) {
- gss_des_crypt(ki, (des_cblock *) ptr, (des_cblock *) ptr,
- left, &ivec, &ivec, encrypt, KG_USAGE_SEAL);
- len -= left;
- }
-
- if (mbn && remain > 0) {
- nptr = mbuf_data(mbn);
- offset = 8 - remain;
- bcopy(ptr + left, tmp, remain); // grab from this mbuf
- bcopy(nptr, tmp + remain, offset); // grab from next mbuf
- gss_des_crypt(ki, (des_cblock *) tmp, (des_cblock *) tmp, 8,
- &ivec, &ivec, encrypt, KG_USAGE_SEAL);
- bcopy(tmp, ptr + left, remain); // return to this mbuf
- bcopy(tmp + remain, nptr, offset); // return to next mbuf
- len -= 8;
- }
- }
-}
-
-/*
- * Encrypt or decrypt data in an NFS mbuf chain with des-cbc.
- */
-static void
-nfs_gss_encrypt_chain(
- gss_key_info *ki,
- struct nfsm_chain *nmc,
- int offset,
- int len,
- int encrypt)
-{
- /*
- * If the length parameter is zero, then we need
- * to use the length from the offset to the current
- * encode/decode offset.
- */
- if (len == 0)
- len = nfsm_chain_offset(nmc) - offset;
-
- return (nfs_gss_encrypt_mchain(ki, nmc->nmc_mhead, offset, len, encrypt));
-}
-
-/*
- * The routines that follow provide abstractions for doing digests and crypto.
- */
-
-static void
-gss_digest_Init(GSS_DIGEST_CTX *ctx, gss_key_info *ki)
-{
- ctx->type = ki->type;
- switch (ki->type) {
- case NFS_GSS_1DES: MD5_DESCBC_Init(&ctx->m_ctx, &ki->ks_u.des.gss_sched);
- break;
- case NFS_GSS_3DES: HMAC_SHA1_DES3KD_Init(&ctx->h_ctx, ki->ks_u.des3.ckey, 0);
- break;
- default:
- printf("gss_digest_Init: Unknown key info type %d\n", ki->type);
- }
-}
-
-static void
-gss_digest_Update(GSS_DIGEST_CTX *ctx, void *data, size_t len)
-{
- switch (ctx->type) {
- case NFS_GSS_1DES: MD5_DESCBC_Update(&ctx->m_ctx, data, len);
- break;
- case NFS_GSS_3DES: HMAC_SHA1_DES3KD_Update(&ctx->h_ctx, data, len);
- break;
- }
-}
-
-static void
-gss_digest_Final(GSS_DIGEST_CTX *ctx, void *digest)
-{
- switch (ctx->type) {
- case NFS_GSS_1DES: MD5_DESCBC_Final(digest, &ctx->m_ctx);
- break;
- case NFS_GSS_3DES: HMAC_SHA1_DES3KD_Final(digest, &ctx->h_ctx);
- break;
- }
-}
-
-static void
-gss_des_crypt(gss_key_info *ki, des_cblock *in, des_cblock *out,
- int32_t len, des_cblock *iv, des_cblock *retiv, int encrypt, int usage)
-{
- switch (ki->type) {
- case NFS_GSS_1DES:
- {
- des_cbc_key_schedule *sched = ((usage == KG_USAGE_SEAL) ?
- &ki->ks_u.des.gss_sched_Ke :
- &ki->ks_u.des.gss_sched);
- des_cbc_encrypt(in, out, len, sched, iv, retiv, encrypt);
- }
- break;
- case NFS_GSS_3DES:
-
- des3_cbc_encrypt(in, out, len, &ki->ks_u.des3.gss_sched, iv, retiv, encrypt);
- break;
- }
-}
-
-static int
-gss_key_init(gss_key_info *ki, uint32_t skeylen)
-{
- size_t i;
- int rc;
- des_cblock k[3];
-
- ki->keybytes = skeylen;
- switch (skeylen) {
- case sizeof(des_cblock):
- ki->type = NFS_GSS_1DES;
- ki->hash_len = MD5_DESCBC_DIGEST_LENGTH;
- ki->ks_u.des.key = (des_cblock *)ki->skey;
- rc = des_cbc_key_sched(ki->ks_u.des.key, &ki->ks_u.des.gss_sched);
- if (rc)
- return (rc);
- for (i = 0; i < ki->keybytes; i++)
- k[0][i] = 0xf0 ^ (*ki->ks_u.des.key)[i];
- rc = des_cbc_key_sched(&k[0], &ki->ks_u.des.gss_sched_Ke);
- break;
- case 3*sizeof(des_cblock):
- ki->type = NFS_GSS_3DES;
- ki->hash_len = SHA_DIGEST_LENGTH;
- ki->ks_u.des3.key = (des_cblock (*)[3])ki->skey;
- des3_derive_key(*ki->ks_u.des3.key, ki->ks_u.des3.ckey,
- KEY_USAGE_DES3_SIGN, KEY_USAGE_LEN);
- rc = des3_cbc_key_sched(*ki->ks_u.des3.key, &ki->ks_u.des3.gss_sched);
- if (rc)
- return (rc);
- break;
- default:
- printf("gss_key_init: Invalid key length %d\n", skeylen);
- rc = EINVAL;
- break;
- }
-
- return (rc);
-}
-