*tailskip = sec_len - data_offset - data_len;
return GSS_S_COMPLETE;
}
+
+/**
+ * gss_krb5_mic_build_sg - Build scatterlist for MIC token operations
+ * @body: xdr_buf containing the message body
+ * @cksum: pointer to checksum area in the token buffer
+ * @cksum_len: length of checksum area
+ * @hdr: pointer to GSS token header
+ * @sg_head: caller-provided scatterlist array; if more than
+ * XDR_BUF_TO_SG_NENTS entries are needed, an overflow
+ * scatterlist is allocated and chained automatically
+ * @sg_overflow: OUT: overflow scatterlist, caller must kfree
+ *
+ * Per RFC 4121 Section 4.2.4, MIC token checksums cover the
+ * message body followed by the token header. The checksum
+ * output or received checksum occupies the first scatterlist
+ * entry. This layout cannot be constructed by
+ * xdr_buf_to_sg_alloc() because the checksum area and the GSS
+ * header lie outside the xdr_buf.
+ *
+ * Returns the number of scatterlist entries on success, or a
+ * negative errno on failure.
+ */
+int gss_krb5_mic_build_sg(const struct xdr_buf *body,
+ void *cksum, unsigned int cksum_len,
+ void *hdr,
+ struct scatterlist *sg_head,
+ struct scatterlist **sg_overflow)
+{
+ struct scatterlist *entry;
+ int body_max, body_nsg, nsg;
+
+ *sg_overflow = NULL;
+
+ body_max = 2;
+ if (body->page_len)
+ body_max += DIV_ROUND_UP(body->page_len +
+ offset_in_page(body->page_base),
+ PAGE_SIZE);
+ nsg = 1 + body_max + 1;
+ if (nsg <= XDR_BUF_TO_SG_NENTS) {
+ sg_init_table(sg_head, nsg);
+ } else {
+ unsigned int overflow_nents =
+ nsg - XDR_BUF_TO_SG_NENTS + 1;
+
+ *sg_overflow = kmalloc_array(overflow_nents,
+ sizeof(**sg_overflow),
+ GFP_NOFS);
+ if (!*sg_overflow)
+ return -ENOMEM;
+
+ sg_init_table(sg_head, XDR_BUF_TO_SG_NENTS);
+ sg_init_table(*sg_overflow, overflow_nents);
+ sg_chain(sg_head, XDR_BUF_TO_SG_NENTS, *sg_overflow);
+ }
+
+ sg_set_buf(&sg_head[0], cksum, cksum_len);
+ body_nsg = xdr_buf_to_sg(body, 0, body->len,
+ sg_next(&sg_head[0]), body_max);
+ if (body_nsg < 0)
+ goto out_err;
+
+ /*
+ * xdr_buf_to_sg marks the last body entry as end-of-list;
+ * clear it so the trailing header entry is reachable.
+ */
+ if (body_nsg > 0) {
+ entry = sg_last(sg_next(&sg_head[0]), body_nsg);
+ sg_unmark_end(entry);
+ entry = sg_next(entry);
+ } else {
+ entry = sg_next(&sg_head[0]);
+ }
+ sg_set_buf(entry, hdr, GSS_KRB5_TOK_HDR_LEN);
+ sg_mark_end(entry);
+ return 1 + body_nsg + 1;
+
+out_err:
+ kfree(*sg_overflow);
+ *sg_overflow = NULL;
+ return body_nsg;
+}
u32 gss_krb5_errno_to_status(int err);
+int gss_krb5_mic_build_sg(const struct xdr_buf *body,
+ void *cksum, unsigned int cksum_len,
+ void *hdr,
+ struct scatterlist *sg_head,
+ struct scatterlist **sg_overflow);
+
u32 gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, struct page **pages);
u32 gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
#include <linux/random.h>
#include <linux/crypto.h>
#include <linux/atomic.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include "gss_krb5_internal.h"
void *krb5_hdr;
u8 *p, flags = 0x00;
- if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
- flags |= 0x01;
+ if (!ctx->initiate)
+ flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
- flags |= 0x04;
+ flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* Per rfc 4121, sec 4.2.6.1, there is no header,
* just start the token.
*ptr++ = 0xffff;
*ptr = 0xffff;
- token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
+ token->len = GSS_KRB5_TOK_HDR_LEN + ctx->krb5e->cksum_len;
return krb5_hdr;
}
gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
struct xdr_netobj *token)
{
- struct crypto_ahash *tfm = ctx->initiate ?
- ctx->initiator_sign : ctx->acceptor_sign;
- struct xdr_netobj cksumobj = {
- .len = ctx->gk5e->cksumlength,
- };
+ const struct krb5_enctype *krb5 = ctx->krb5e;
+ struct crypto_shash *shash = ctx->initiate ?
+ ctx->initiator_sign_shash : ctx->acceptor_sign_shash;
+ unsigned int cksum_len = krb5->cksum_len;
+ struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow;
__be64 seq_send_be64;
void *krb5_hdr;
time64_t now;
+ ssize_t ret;
+ int nsg;
dprintk("RPC: %s\n", __func__);
seq_send_be64 = cpu_to_be64(atomic64_fetch_inc(&ctx->seq_send64));
memcpy(krb5_hdr + 8, (char *) &seq_send_be64, 8);
- cksumobj.data = krb5_hdr + GSS_KRB5_TOK_HDR_LEN;
- if (gss_krb5_checksum(tfm, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
- text, 0, &cksumobj))
+ /*
+ * The checksum is written directly into the token buffer.
+ * This is safe: crypto_krb5_get_mic uses shash (software
+ * hash), so the scatterlist is never DMA-mapped.
+ */
+ nsg = gss_krb5_mic_build_sg(text,
+ krb5_hdr + GSS_KRB5_TOK_HDR_LEN,
+ cksum_len, krb5_hdr,
+ sg_head, &sg_overflow);
+ if (nsg < 0)
+ return GSS_S_FAILURE;
+
+ ret = crypto_krb5_get_mic(krb5, shash, NULL, sg_head, nsg,
+ cksum_len + text->len +
+ GSS_KRB5_TOK_HDR_LEN,
+ cksum_len,
+ text->len + GSS_KRB5_TOK_HDR_LEN);
+ kfree(sg_overflow);
+ if (ret < 0)
return GSS_S_FAILURE;
now = ktime_get_real_seconds();