Backport upstream stable fixes for newly RHEL-Affected Important CVEs (patches 1113-1120, 1122-1126), closest-to-6.12 base, verified zero-fuzz full %prep replay: CVE-2026-63886 63887 63888 (iscsi target), 63899 63956 (usb-serial), 63917 (ip6 vti), 63922 63924 (ipv6 exthdrs), 63939 (kvm sev), 63994 (tunnels), 64017 (blk-mq), 64026 (rxrpc), 64030 (mac80211) CVE-2026-63950 (mm/rmap) dropped: not applicable to el10 (try_to_unmap_one has no nr_pages batching variable; the stale-nr_pages bug the fix targets cannot occur).
655 lines
22 KiB
Diff
655 lines
22 KiB
Diff
From b94a6ccbaf1104dd980150a65fdeb2f69d17d2f5 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 29 May 2026 17:42:07 -0400
|
|
Subject: [PATCH] rxrpc: Fix DATA decrypt vs splice() by copying data to buffer
|
|
in recvmsg
|
|
|
|
[ Upstream commit d2bc90cf6c75cb96d2ce549be6c35efa3099d25b ]
|
|
|
|
This improves the fix for CVE-2026-43500.
|
|
|
|
Fix the pagecache corruption from in-place decryption of a DATA packet
|
|
transmitted locally by splice() by getting rid of the packet sharing in the
|
|
I/O thread and unconditionally extracting the packet content into a bounce
|
|
buffer in which the buffer is decrypted. recvmsg() (or the kernel
|
|
equivalent) then copies the data from the bounce buffer to the destination
|
|
buffer. The sk_buff then remains unmodified.
|
|
|
|
This has an additional advantage in that the packet is then arranged in the
|
|
buffer with the correct alignment required for the crypto algorithms to
|
|
process directly. The performance of the crypto does seem to be a little
|
|
faster and, surprisingly, the unencrypted performance doesn't seem to
|
|
change much - possibly due to removing complexity from the I/O thread.
|
|
|
|
Yet another advantage is that the I/O thread doesn't have to copy packets
|
|
which would slow down packet distribution, ACK generation, etc..
|
|
|
|
The buffer belongs to the call and is allocated initially at 2K,
|
|
sufficiently large to hold a whole jumbo subpacket, but the buffer will be
|
|
increased in size if needed. However, to take this work, MSG_PEEK may
|
|
cause a later packet to be decrypted into the buffer, in which case the
|
|
earlier one will need re-decrypting for a subsequent recvmsg().
|
|
|
|
Note that rx_pkt_offset may legitimately see 0 as a valid offset now, so
|
|
switch to using USHRT_MAX to indicate an invalid offset.
|
|
|
|
Note also that I would generally prefer to replace the buffers of the
|
|
current sk_buff with a new kmalloc'd buffer of the right size, ditching the
|
|
old data and frags as this makes the handling of MSG_PEEK easier and
|
|
removes the re-decryption issue, but this looks like quite a complicated
|
|
thing to achieve. skb_morph() looks half way to what I want, but I don't
|
|
want to have to allocate a new sk_buff.
|
|
|
|
Fixes: d0d5c0cd1e71 ("rxrpc: Use skb_unshare() rather than skb_cow_data()")
|
|
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
|
|
Closes: https://lore.kernel.org/r/afKV2zGR6rrelPC7@v4bel/
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
cc: Simon Horman <horms@kernel.org>
|
|
cc: Jiayuan Chen <jiayuan.chen@linux.dev>
|
|
cc: linux-afs@lists.infradead.org
|
|
Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
|
|
Tested-by: Marc Dionne <marc.dionne@auristor.com>
|
|
Link: https://patch.msgid.link/20260515230516.2718212-3-dhowells@redhat.com
|
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
|
Stable-dep-of: 8bfab4b6ffc2 ("rxrpc: Fix RESPONSE packet verification to extract skb to a linear buffer")
|
|
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
|
|
CVE-2026-64026
|
|
|
|
Backported from linux-6.12.y commit b94a6ccbaf1104dd980150a65fdeb2f69d17d2f5
|
|
(mainline d2bc90cf6c75cb96d2ce549be6c35efa3099d25b).
|
|
|
|
Adaptations for the a10 kernel-6.12.0-211.34.1.el10_2 tree:
|
|
- rxgk: The 6.12.y stable commit touches only 6 files because the yfs-rxgk
|
|
(GSSAPI) security class does not exist in 6.12.y stable. a10 DOES ship a
|
|
functional rxgk security class, so the rxgk.c and rxgk_common.h hunks from
|
|
the mainline commit are included as well (otherwise rxgk DATA would be read
|
|
undecrypted from the bounce buffer and remain vulnerable to the splice
|
|
corruption). rxgk_verify_packet_encrypted() is adapted to a10, which lacks
|
|
the upstream crypto_krb5_check_data_len() pre-check (separate commit).
|
|
- net/rxrpc/call_event.c: a10 carries the newer rx_queue batching rework, so
|
|
the packet-unshare block that this fix removes has a different shape
|
|
(skb_cloned() only, rxrpc_skb_put_call_rx); it is collapsed to a plain
|
|
rxrpc_input_call_packet(call, skb) as upstream does.
|
|
- net/rxrpc/rxkad.c: a10's rxkad_verify_packet_2() lacks the upstream
|
|
round_down() length alignment and the crypto_skcipher_decrypt() return-value
|
|
check (separate commits); those are preserved as-is (not introduced here).
|
|
- net/rxrpc/recvmsg.c: a10 lacks the upstream kdebug("verify = %d") line;
|
|
preserved as-is.
|
|
- net/rxrpc/ar-internal.h, call_object.c: context differs only by a10's extra
|
|
rx_queue field/init and the rxrpc_cleanup_rx_buffers() rename.
|
|
|
|
Vulnerable pre-image confirmed present in a10 (in-place skb decryption via
|
|
skb_to_sgvec in rxkad/rxgk, RXRPC_RX_VERIFIED, rx_pkt_offset==0 recvmsg path,
|
|
call_event unshare copy). Applies zero-fuzz on the a10 tree.
|
|
|
|
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
|
|
index 2baa99b..a08e45c 100644
|
|
--- a/net/rxrpc/ar-internal.h
|
|
+++ b/net/rxrpc/ar-internal.h
|
|
@@ -213,8 +213,6 @@ struct rxrpc_skb_priv {
|
|
struct {
|
|
u16 offset; /* Offset of data */
|
|
u16 len; /* Length of data */
|
|
- u8 flags;
|
|
-#define RXRPC_RX_VERIFIED 0x01
|
|
};
|
|
struct {
|
|
rxrpc_seq_t first_ack; /* First packet in acks table */
|
|
@@ -774,6 +772,11 @@ struct rxrpc_call {
|
|
struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */
|
|
struct sk_buff_head rx_queue; /* Queue of packets for this call to receive */
|
|
struct sk_buff_head rx_oos_queue; /* Queue of out of sequence packets */
|
|
+ void *rx_dec_buffer; /* Decryption buffer */
|
|
+ unsigned short rx_dec_bsize; /* rx_dec_buffer size */
|
|
+ unsigned short rx_dec_offset; /* Decrypted packet data offset */
|
|
+ unsigned short rx_dec_len; /* Decrypted packet data len */
|
|
+ rxrpc_seq_t rx_dec_seq; /* Packet in decryption buffer */
|
|
|
|
rxrpc_seq_t rx_highest_seq; /* Higest sequence number received */
|
|
rxrpc_seq_t rx_consumed; /* Highest packet consumed */
|
|
diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
|
|
index fdd6832..fec59d9 100644
|
|
--- a/net/rxrpc/call_event.c
|
|
+++ b/net/rxrpc/call_event.c
|
|
@@ -332,25 +332,7 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
|
|
|
|
saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
|
|
|
|
- if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA &&
|
|
- sp->hdr.securityIndex != 0 &&
|
|
- skb_cloned(skb)) {
|
|
- /* Unshare the packet so that it can be
|
|
- * modified by in-place decryption.
|
|
- */
|
|
- struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
|
|
-
|
|
- if (nskb) {
|
|
- rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
|
|
- rxrpc_input_call_packet(call, nskb);
|
|
- rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
|
|
- } else {
|
|
- /* OOM - Drop the packet. */
|
|
- rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
|
|
- }
|
|
- } else {
|
|
- rxrpc_input_call_packet(call, skb);
|
|
- }
|
|
+ rxrpc_input_call_packet(call, skb);
|
|
rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
|
|
did_receive = true;
|
|
}
|
|
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
|
|
index 918f41d..ffe0a89 100644
|
|
--- a/net/rxrpc/call_object.c
|
|
+++ b/net/rxrpc/call_object.c
|
|
@@ -152,6 +152,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp,
|
|
spin_lock_init(&call->notify_lock);
|
|
refcount_set(&call->ref, 1);
|
|
call->debug_id = debug_id;
|
|
+ call->rx_pkt_offset = USHRT_MAX;
|
|
call->tx_total_len = -1;
|
|
call->tx_jumbo_max = 1;
|
|
call->next_rx_timo = 20 * HZ;
|
|
@@ -553,6 +554,7 @@ static void rxrpc_cleanup_rx_buffers(struct rxrpc_call *call)
|
|
rxrpc_purge_queue(&call->recvmsg_queue);
|
|
rxrpc_purge_queue(&call->rx_queue);
|
|
rxrpc_purge_queue(&call->rx_oos_queue);
|
|
+ kfree(call->rx_dec_buffer);
|
|
}
|
|
|
|
/*
|
|
diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c
|
|
index 0a260df..7a26c60 100644
|
|
--- a/net/rxrpc/insecure.c
|
|
+++ b/net/rxrpc/insecure.c
|
|
@@ -32,9 +32,6 @@ static int none_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb)
|
|
|
|
static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
|
|
{
|
|
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
-
|
|
- sp->flags |= RXRPC_RX_VERIFIED;
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
|
|
index 7fa7e77..fc01ee3 100644
|
|
--- a/net/rxrpc/recvmsg.c
|
|
+++ b/net/rxrpc/recvmsg.c
|
|
@@ -147,15 +147,52 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call)
|
|
}
|
|
|
|
/*
|
|
- * Decrypt and verify a DATA packet.
|
|
+ * Decrypt and verify a DATA packet. The content of the packet is pulled out
|
|
+ * into a flat buffer rather than decrypting in place in the skbuff. This also
|
|
+ * has the advantage of aligning the buffer correctly for the crypto routines.
|
|
+ *
|
|
+ * We keep track of the sequence number of the packet currently decrypted into
|
|
+ * the buffer in ->rx_dec_seq. If MSG_PEEK is used and steps onto a new
|
|
+ * packet, subsequent recvmsg() calls will have to go back and re-decrypt the
|
|
+ * current packet.
|
|
*/
|
|
static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb)
|
|
{
|
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
+ int ret;
|
|
|
|
- if (sp->flags & RXRPC_RX_VERIFIED)
|
|
- return 0;
|
|
- return call->security->verify_packet(call, skb);
|
|
+ if (sp->len > call->rx_dec_bsize) {
|
|
+ /* Make sure we can hold a 1412-byte jumbo subpacket and make
|
|
+ * sure that the buffer size is aligned to a crypto blocksize.
|
|
+ */
|
|
+ size_t size = clamp(round_up(sp->len, 32), 2048, 65535);
|
|
+ void *buffer = krealloc(call->rx_dec_buffer, size, GFP_NOFS);
|
|
+
|
|
+ if (!buffer)
|
|
+ return -ENOMEM;
|
|
+ call->rx_dec_buffer = buffer;
|
|
+ call->rx_dec_bsize = size;
|
|
+ }
|
|
+
|
|
+ ret = -EFAULT;
|
|
+ if (skb_copy_bits(skb, sp->offset, call->rx_dec_buffer, sp->len) < 0)
|
|
+ goto err;
|
|
+
|
|
+ call->rx_dec_offset = 0;
|
|
+ call->rx_dec_len = sp->len;
|
|
+ call->rx_dec_seq = sp->hdr.seq;
|
|
+ ret = call->security->verify_packet(call, skb);
|
|
+ if (ret < 0)
|
|
+ goto err;
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ kfree(call->rx_dec_buffer);
|
|
+ call->rx_dec_buffer = NULL;
|
|
+ call->rx_dec_bsize = 0;
|
|
+ call->rx_dec_offset = 0;
|
|
+ call->rx_dec_len = 0;
|
|
+ return ret;
|
|
}
|
|
|
|
/*
|
|
@@ -283,16 +320,21 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
|
|
if (msg)
|
|
sock_recv_timestamp(msg, sock->sk, skb);
|
|
|
|
- if (rx_pkt_offset == 0) {
|
|
+ if (call->rx_dec_seq != sp->hdr.seq ||
|
|
+ !call->rx_dec_buffer) {
|
|
ret2 = rxrpc_verify_data(call, skb);
|
|
trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq,
|
|
- sp->offset, sp->len, ret2);
|
|
+ call->rx_dec_offset,
|
|
+ call->rx_dec_len, ret2);
|
|
if (ret2 < 0) {
|
|
ret = ret2;
|
|
goto out;
|
|
}
|
|
- rx_pkt_offset = sp->offset;
|
|
- rx_pkt_len = sp->len;
|
|
+ }
|
|
+
|
|
+ if (rx_pkt_offset == USHRT_MAX) {
|
|
+ rx_pkt_offset = call->rx_dec_offset;
|
|
+ rx_pkt_len = call->rx_dec_len;
|
|
} else {
|
|
trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq,
|
|
rx_pkt_offset, rx_pkt_len, 0);
|
|
@@ -304,10 +346,10 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
|
|
if (copy > remain)
|
|
copy = remain;
|
|
if (copy > 0) {
|
|
- ret2 = skb_copy_datagram_iter(skb, rx_pkt_offset, iter,
|
|
- copy);
|
|
- if (ret2 < 0) {
|
|
- ret = ret2;
|
|
+ ret2 = copy_to_iter(call->rx_dec_buffer + rx_pkt_offset,
|
|
+ copy, iter);
|
|
+ if (ret2 != copy) {
|
|
+ ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
@@ -328,7 +370,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
|
|
/* The whole packet has been transferred. */
|
|
if (sp->hdr.flags & RXRPC_LAST_PACKET)
|
|
ret = 1;
|
|
- rx_pkt_offset = 0;
|
|
+ rx_pkt_offset = USHRT_MAX;
|
|
rx_pkt_len = 0;
|
|
|
|
skb = skb_peek_next(skb, &call->recvmsg_queue);
|
|
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
|
|
index 8d17b49..b9d2da9 100644
|
|
--- a/net/rxrpc/rxgk.c
|
|
+++ b/net/rxrpc/rxgk.c
|
|
@@ -473,8 +473,9 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
|
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
struct rxgk_header *hdr;
|
|
struct krb5_buffer metadata;
|
|
- unsigned int offset = sp->offset, len = sp->len;
|
|
+ unsigned int len = call->rx_dec_len;
|
|
size_t data_offset = 0, data_len = len;
|
|
+ void *data = call->rx_dec_buffer, *p = data;
|
|
u32 ac = 0;
|
|
int ret = -ENOMEM;
|
|
|
|
@@ -496,16 +497,15 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
|
|
|
|
metadata.len = sizeof(*hdr);
|
|
metadata.data = hdr;
|
|
- ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata,
|
|
- skb, &offset, &len, &ac);
|
|
+ ret = rxgk_verify_mic(gk->krb5, gk->rx_Kc, &metadata, &p, &len, &ac);
|
|
kfree(hdr);
|
|
if (ret < 0) {
|
|
if (ret != -ENOMEM)
|
|
rxrpc_abort_eproto(call, skb, ac,
|
|
rxgk_abort_1_verify_mic_eproto);
|
|
} else {
|
|
- sp->offset = offset;
|
|
- sp->len = len;
|
|
+ call->rx_dec_offset = p - data;
|
|
+ call->rx_dec_len = len;
|
|
}
|
|
|
|
put_gk:
|
|
@@ -522,49 +522,46 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
- struct rxgk_header hdr;
|
|
- unsigned int offset = sp->offset, len = sp->len;
|
|
+ struct rxgk_header *hdr;
|
|
+ unsigned int offset = 0, len = call->rx_dec_len;
|
|
+ void *data = call->rx_dec_buffer, *p = data;
|
|
int ret;
|
|
u32 ac = 0;
|
|
|
|
_enter("");
|
|
|
|
- ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac);
|
|
+ ret = rxgk_decrypt(gk->krb5, gk->rx_enc, &p, &len, &ac);
|
|
if (ret < 0) {
|
|
if (ret != -ENOMEM)
|
|
rxrpc_abort_eproto(call, skb, ac, rxgk_abort_2_decrypt_eproto);
|
|
goto error;
|
|
}
|
|
+ offset = p - data;
|
|
|
|
- if (len < sizeof(hdr)) {
|
|
+ if (len < sizeof(*hdr)) {
|
|
ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
|
|
rxgk_abort_2_short_header);
|
|
goto error;
|
|
}
|
|
|
|
/* Extract the header from the skb */
|
|
- ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr));
|
|
- if (ret < 0) {
|
|
- ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
|
|
- rxgk_abort_2_short_encdata);
|
|
- goto error;
|
|
- }
|
|
- offset += sizeof(hdr);
|
|
- len -= sizeof(hdr);
|
|
-
|
|
- if (ntohl(hdr.epoch) != call->conn->proto.epoch ||
|
|
- ntohl(hdr.cid) != call->cid ||
|
|
- ntohl(hdr.call_number) != call->call_id ||
|
|
- ntohl(hdr.seq) != sp->hdr.seq ||
|
|
- ntohl(hdr.sec_index) != call->security_ix ||
|
|
- ntohl(hdr.data_len) > len) {
|
|
+ hdr = data + offset;
|
|
+ offset += sizeof(*hdr);
|
|
+ len -= sizeof(*hdr);
|
|
+
|
|
+ if (ntohl(hdr->epoch) != call->conn->proto.epoch ||
|
|
+ ntohl(hdr->cid) != call->cid ||
|
|
+ ntohl(hdr->call_number) != call->call_id ||
|
|
+ ntohl(hdr->seq) != sp->hdr.seq ||
|
|
+ ntohl(hdr->sec_index) != call->security_ix ||
|
|
+ ntohl(hdr->data_len) > len) {
|
|
ret = rxrpc_abort_eproto(call, skb, RXGK_SEALEDINCON,
|
|
rxgk_abort_2_short_data);
|
|
goto error;
|
|
}
|
|
|
|
- sp->offset = offset;
|
|
- sp->len = ntohl(hdr.data_len);
|
|
+ call->rx_dec_offset = offset;
|
|
+ call->rx_dec_len = ntohl(hdr->data_len);
|
|
ret = 0;
|
|
error:
|
|
rxgk_put(gk);
|
|
diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h
|
|
index 80164d8..bae8b98 100644
|
|
--- a/net/rxrpc/rxgk_common.h
|
|
+++ b/net/rxrpc/rxgk_common.h
|
|
@@ -104,6 +104,49 @@ int rxgk_decrypt_skb(const struct krb5_enctype *krb5,
|
|
return ret;
|
|
}
|
|
|
|
+/*
|
|
+ * Apply decryption and checksumming functions a flat data buffer. The data
|
|
+ * point and length are updated to reflect the actual content of the encrypted
|
|
+ * region.
|
|
+ */
|
|
+static inline int rxgk_decrypt(const struct krb5_enctype *krb5,
|
|
+ struct crypto_aead *aead,
|
|
+ void **_data, unsigned int *_len,
|
|
+ int *_error_code)
|
|
+{
|
|
+ struct scatterlist sg[1];
|
|
+ size_t offset = 0, len = *_len;
|
|
+ int ret;
|
|
+
|
|
+ sg_init_one(sg, *_data, len);
|
|
+
|
|
+ ret = crypto_krb5_decrypt(krb5, aead, sg, 1, &offset, &len);
|
|
+ switch (ret) {
|
|
+ case 0:
|
|
+ if (offset & 3) {
|
|
+ *_error_code = RXGK_INCONSISTENCY;
|
|
+ ret = -EPROTO;
|
|
+ break;
|
|
+ }
|
|
+ *_data += offset;
|
|
+ *_len = len;
|
|
+ break;
|
|
+ case -EBADMSG: /* Checksum mismatch. */
|
|
+ case -EPROTO:
|
|
+ *_error_code = RXGK_SEALEDINCON;
|
|
+ break;
|
|
+ case -EMSGSIZE:
|
|
+ *_error_code = RXGK_PACKETSHORT;
|
|
+ break;
|
|
+ case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */
|
|
+ default:
|
|
+ *_error_code = RXGK_INCONSISTENCY;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/*
|
|
* Check the MIC on a region of an skbuff. The offset and length are updated
|
|
* to reflect the actual content of the secure region.
|
|
@@ -147,3 +190,42 @@ int rxgk_verify_mic_skb(const struct krb5_enctype *krb5,
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Check the MIC on a flat buffer. The data pointer and length are updated to
|
|
+ * reflect the actual content of the secure region.
|
|
+ */
|
|
+static inline
|
|
+int rxgk_verify_mic(const struct krb5_enctype *krb5,
|
|
+ struct crypto_shash *shash,
|
|
+ const struct krb5_buffer *metadata,
|
|
+ void **_data, unsigned int *_len,
|
|
+ u32 *_error_code)
|
|
+{
|
|
+ struct scatterlist sg[1];
|
|
+ size_t offset = 0, len = *_len;
|
|
+ int ret;
|
|
+
|
|
+ sg_init_one(sg, *_data, len);
|
|
+
|
|
+ ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, 1, &offset, &len);
|
|
+ switch (ret) {
|
|
+ case 0:
|
|
+ *_data += offset;
|
|
+ *_len = len;
|
|
+ break;
|
|
+ case -EBADMSG: /* Checksum mismatch */
|
|
+ case -EPROTO:
|
|
+ *_error_code = RXGK_SEALEDINCON;
|
|
+ break;
|
|
+ case -EMSGSIZE:
|
|
+ *_error_code = RXGK_PACKETSHORT;
|
|
+ break;
|
|
+ case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */
|
|
+ default:
|
|
+ *_error_code = RXGK_INCONSISTENCY;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
|
|
index 3657c06..cf780ba 100644
|
|
--- a/net/rxrpc/rxkad.c
|
|
+++ b/net/rxrpc/rxkad.c
|
|
@@ -425,27 +425,24 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
|
|
rxrpc_seq_t seq,
|
|
struct skcipher_request *req)
|
|
{
|
|
- struct rxkad_level1_hdr sechdr;
|
|
+ struct rxkad_level1_hdr *sechdr;
|
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
struct rxrpc_crypt iv;
|
|
- struct scatterlist sg[16];
|
|
- u32 data_size, buf;
|
|
+ struct scatterlist sg[1];
|
|
+ void *data = call->rx_dec_buffer;
|
|
+ u32 len = sp->len, data_size, buf;
|
|
u16 check;
|
|
- int ret;
|
|
|
|
_enter("");
|
|
|
|
- if (sp->len < 8)
|
|
+ if (len < 8)
|
|
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
|
|
rxkad_abort_1_short_header);
|
|
|
|
/* Decrypt the skbuff in-place. TODO: We really want to decrypt
|
|
* directly into the target buffer.
|
|
*/
|
|
- sg_init_table(sg, ARRAY_SIZE(sg));
|
|
- ret = skb_to_sgvec(skb, sg, sp->offset, 8);
|
|
- if (unlikely(ret < 0))
|
|
- return ret;
|
|
+ sg_init_one(sg, data, len);
|
|
|
|
/* start the decryption afresh */
|
|
memset(&iv, 0, sizeof(iv));
|
|
@@ -457,13 +454,11 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
|
|
skcipher_request_zero(req);
|
|
|
|
/* Extract the decrypted packet length */
|
|
- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
|
|
- return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
|
|
- rxkad_abort_1_short_encdata);
|
|
- sp->offset += sizeof(sechdr);
|
|
- sp->len -= sizeof(sechdr);
|
|
+ sechdr = data;
|
|
+ call->rx_dec_offset = sizeof(*sechdr);
|
|
+ len -= sizeof(*sechdr);
|
|
|
|
- buf = ntohl(sechdr.data_size);
|
|
+ buf = ntohl(sechdr->data_size);
|
|
data_size = buf & 0xffff;
|
|
|
|
check = buf >> 16;
|
|
@@ -472,10 +467,10 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
|
|
if (check != 0)
|
|
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
|
|
rxkad_abort_1_short_check);
|
|
- if (data_size > sp->len)
|
|
+ if (data_size > len)
|
|
return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
|
|
rxkad_abort_1_short_data);
|
|
- sp->len = data_size;
|
|
+ call->rx_dec_len = data_size;
|
|
|
|
_leave(" = 0 [dlen=%x]", data_size);
|
|
return 0;
|
|
@@ -489,40 +484,24 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
|
|
struct skcipher_request *req)
|
|
{
|
|
const struct rxrpc_key_token *token;
|
|
- struct rxkad_level2_hdr sechdr;
|
|
+ struct rxkad_level2_hdr *sechdr;
|
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
struct rxrpc_crypt iv;
|
|
- struct scatterlist _sg[4], *sg;
|
|
- u32 data_size, buf;
|
|
+ struct scatterlist sg[1];
|
|
+ void *data = call->rx_dec_buffer;
|
|
+ u32 len = sp->len, data_size, buf;
|
|
u16 check;
|
|
- int nsg, ret;
|
|
|
|
- _enter(",{%d}", sp->len);
|
|
+ _enter(",{%d}", len);
|
|
|
|
- if (sp->len < 8)
|
|
+ if (len < 8)
|
|
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
|
|
rxkad_abort_2_short_header);
|
|
|
|
- /* Decrypt the skbuff in-place. TODO: We really want to decrypt
|
|
- * directly into the target buffer.
|
|
+ /* Decrypt in place in the call's decryption buffer. TODO: We really
|
|
+ * want to decrypt directly into the target buffer.
|
|
*/
|
|
- sg = _sg;
|
|
- nsg = skb_shinfo(skb)->nr_frags + 1;
|
|
- if (nsg <= 4) {
|
|
- nsg = 4;
|
|
- } else {
|
|
- sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO);
|
|
- if (!sg)
|
|
- return -ENOMEM;
|
|
- }
|
|
-
|
|
- sg_init_table(sg, nsg);
|
|
- ret = skb_to_sgvec(skb, sg, sp->offset, sp->len);
|
|
- if (unlikely(ret < 0)) {
|
|
- if (sg != _sg)
|
|
- kfree(sg);
|
|
- return ret;
|
|
- }
|
|
+ sg_init_one(sg, data, len);
|
|
|
|
/* decrypt from the session key */
|
|
token = call->conn->key->payload.data[0];
|
|
@@ -530,20 +509,16 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
|
|
|
|
skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
|
|
skcipher_request_set_callback(req, 0, NULL, NULL);
|
|
- skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x);
|
|
+ skcipher_request_set_crypt(req, sg, sg, len, iv.x);
|
|
crypto_skcipher_decrypt(req);
|
|
skcipher_request_zero(req);
|
|
- if (sg != _sg)
|
|
- kfree(sg);
|
|
|
|
/* Extract the decrypted packet length */
|
|
- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
|
|
- return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
|
|
- rxkad_abort_2_short_len);
|
|
- sp->offset += sizeof(sechdr);
|
|
- sp->len -= sizeof(sechdr);
|
|
+ sechdr = data;
|
|
+ call->rx_dec_offset = sizeof(*sechdr);
|
|
+ len -= sizeof(*sechdr);
|
|
|
|
- buf = ntohl(sechdr.data_size);
|
|
+ buf = ntohl(sechdr->data_size);
|
|
data_size = buf & 0xffff;
|
|
|
|
check = buf >> 16;
|
|
@@ -553,17 +528,18 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
|
|
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
|
|
rxkad_abort_2_short_check);
|
|
|
|
- if (data_size > sp->len)
|
|
+ if (data_size > len)
|
|
return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
|
|
rxkad_abort_2_short_data);
|
|
|
|
- sp->len = data_size;
|
|
+ call->rx_dec_len = data_size;
|
|
_leave(" = 0 [dlen=%x]", data_size);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
- * Verify the security on a received packet and the subpackets therein.
|
|
+ * Verify the security on a received (sub)packet. If the packet needs
|
|
+ * modifying (e.g. decrypting), it must be copied.
|
|
*/
|
|
static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
|
|
{
|