mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 23:22:31 +02:00
Merge branch 'rxrpc-better-fix-for-data-response-decrypt-vs-splice'
David Howells says: ==================== rxrpc: Better fix for DATA/RESPONSE decrypt vs splice() Here are two patches containing better fixes for the in-place decryption of DATA and RESPONSE packets that can corrupt pagecache spliced into UDP packets and sent to an AF_RXRPC server [CVE-2026-43500], plus a patch to precheck the length of rxgk-secured DATA packets. Of the main patches, one patch fixes DATA decryption by having recvmsg unconditionally extract the data into a flat bounce buffer and, if need be, decrypt it there. It doesn't seem to cause a performance problem to do this even on unencrypted packets; for encrypted packets it makes sure the content is correctly aligned for crypto which seems to get a small performance gain. Further, it means that DATA packets are no longer copied in the I/O thread, avoiding a slowdown of the protocol engine that runs there. The other main patch fixes RESPONSE decryption by having the connection event handler worker copy the data to a flat buffer and, again, decrypt it there. This simplifies RESPONSE handling. With these two fixes, the data content of the received sk_buff no longer gets altered. ==================== Link: https://patch.msgid.link/20260515230516.2718212-1-dhowells@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
7769d17e02
|
|
@ -158,13 +158,22 @@ returned.
|
|||
When a message has been received, the location and size of the data with the
|
||||
message can be determined by calling::
|
||||
|
||||
void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_offset, size_t *_len);
|
||||
int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_offset, size_t *_len);
|
||||
|
||||
The caller provides the offset and length of the message to the function, which
|
||||
then alters those values to indicate the region containing the data (plus any
|
||||
padding). It is up to the caller to determine how much padding there is.
|
||||
padding). It is up to the caller to determine how much padding there is. The
|
||||
function returns an error if the length is too small or if the mode is
|
||||
unsupported. An additional function::
|
||||
|
||||
int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t len, size_t min_content);
|
||||
|
||||
is provided to just do a basic check that the decrypted/verified message would
|
||||
have a sufficient minimum payload.
|
||||
|
||||
Preparation Functions
|
||||
---------------------
|
||||
|
|
|
|||
|
|
@ -134,27 +134,69 @@ EXPORT_SYMBOL(crypto_krb5_how_much_data);
|
|||
* Find the offset and size of the data in a secure message so that this
|
||||
* information can be used in the metadata buffer which will get added to the
|
||||
* digest by crypto_krb5_verify_mic().
|
||||
*
|
||||
* Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
|
||||
* the mode is unsupported.
|
||||
*/
|
||||
void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_offset, size_t *_len)
|
||||
int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_offset, size_t *_len)
|
||||
{
|
||||
switch (mode) {
|
||||
case KRB5_CHECKSUM_MODE:
|
||||
if (*_len < krb5->cksum_len)
|
||||
return -EBADMSG;
|
||||
*_offset += krb5->cksum_len;
|
||||
*_len -= krb5->cksum_len;
|
||||
return;
|
||||
return 0;
|
||||
case KRB5_ENCRYPT_MODE:
|
||||
if (*_len < krb5->conf_len + krb5->cksum_len)
|
||||
return -EBADMSG;
|
||||
*_offset += krb5->conf_len;
|
||||
*_len -= krb5->conf_len + krb5->cksum_len;
|
||||
return;
|
||||
return 0;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
|
||||
|
||||
/**
|
||||
* crypto_krb5_check_data_len - Check a message is big enough
|
||||
* @krb5: The encoding to use.
|
||||
* @mode: Mode of operation.
|
||||
* @len: The length of the secure blob.
|
||||
* @min_content: Minimum length of the content inside the blob.
|
||||
*
|
||||
* Check that a message is large enough to hold whatever bits the encryption
|
||||
* type wants to glue on (nonce, checksum) plus a minimum amount of content.
|
||||
*
|
||||
* Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
|
||||
* the mode is unsupported.
|
||||
*/
|
||||
int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t len, size_t min_content)
|
||||
{
|
||||
switch (mode) {
|
||||
case KRB5_CHECKSUM_MODE:
|
||||
if (len < krb5->cksum_len ||
|
||||
len - krb5->cksum_len < min_content)
|
||||
return -EBADMSG;
|
||||
return 0;
|
||||
case KRB5_ENCRYPT_MODE:
|
||||
if (len < krb5->conf_len + krb5->cksum_len ||
|
||||
len - (krb5->conf_len + krb5->cksum_len) < min_content)
|
||||
return -EBADMSG;
|
||||
return 0;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(crypto_krb5_check_data_len);
|
||||
|
||||
/*
|
||||
* Prepare the encryption with derived key data.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -121,9 +121,12 @@ size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
|
|||
size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_buffer_size, size_t *_offset);
|
||||
void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_offset, size_t *_len);
|
||||
int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t *_offset, size_t *_len);
|
||||
int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
|
||||
enum krb5_crypto_mode mode,
|
||||
size_t len, size_t min_content);
|
||||
struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
|
||||
const struct krb5_buffer *TK,
|
||||
u32 usage, gfp_t gfp);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \
|
||||
EM(rxkad_abort_resp_version, "rxkad-resp-version") \
|
||||
/* RxGK security errors */ \
|
||||
EM(rxgk_abort_1_short_header, "rxgk1-short-hdr") \
|
||||
EM(rxgk_abort_1_verify_mic_eproto, "rxgk1-vfy-mic-eproto") \
|
||||
EM(rxgk_abort_2_decrypt_eproto, "rxgk2-dec-eproto") \
|
||||
EM(rxgk_abort_2_short_data, "rxgk2-short-data") \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -309,15 +307,16 @@ struct rxrpc_security {
|
|||
struct sk_buff *challenge);
|
||||
|
||||
/* verify a response */
|
||||
int (*verify_response)(struct rxrpc_connection *,
|
||||
struct sk_buff *);
|
||||
int (*verify_response)(struct rxrpc_connection *conn,
|
||||
struct sk_buff *response_skb,
|
||||
void *response, unsigned int len);
|
||||
|
||||
/* clear connection security */
|
||||
void (*clear)(struct rxrpc_connection *);
|
||||
|
||||
/* Default ticket -> key decoder */
|
||||
int (*default_decode_ticket)(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||
unsigned int ticket_offset, unsigned int ticket_len,
|
||||
void *ticket, unsigned int ticket_len,
|
||||
struct key **_key);
|
||||
};
|
||||
|
||||
|
|
@ -774,6 +773,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 */
|
||||
|
|
|
|||
|
|
@ -332,27 +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) ||
|
||||
skb_has_frag_list(skb) ||
|
||||
skb_has_shared_frag(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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -243,28 +243,22 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call)
|
|||
static int rxrpc_verify_response(struct rxrpc_connection *conn,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
|
||||
void *buffer;
|
||||
int ret;
|
||||
|
||||
if (skb_cloned(skb) || skb_has_frag_list(skb) ||
|
||||
skb_has_shared_frag(skb)) {
|
||||
/* Copy the packet if shared so that we can do in-place
|
||||
* decryption.
|
||||
*/
|
||||
struct sk_buff *nskb = skb_copy(skb, GFP_NOFS);
|
||||
buffer = kmalloc(len, GFP_NOFS);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (nskb) {
|
||||
rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
|
||||
ret = conn->security->verify_response(conn, nskb);
|
||||
rxrpc_free_skb(nskb, rxrpc_skb_put_response_copy);
|
||||
} else {
|
||||
/* OOM - Drop the packet. */
|
||||
rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
ret = conn->security->verify_response(conn, skb);
|
||||
}
|
||||
ret = skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), buffer, len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = conn->security->verify_response(conn, skb, buffer, len);
|
||||
|
||||
out:
|
||||
kfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -57,9 +54,10 @@ static int none_sendmsg_respond_to_challenge(struct sk_buff *challenge,
|
|||
}
|
||||
|
||||
static int none_verify_response(struct rxrpc_connection *conn,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *response_skb,
|
||||
void *response, unsigned int len)
|
||||
{
|
||||
return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
|
||||
return rxrpc_abort_conn(conn, response_skb, RX_PROTOCOL_ERROR, -EPROTO,
|
||||
rxrpc_eproto_rxnull_response);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
160
net/rxrpc/rxgk.c
160
net/rxrpc/rxgk.c
|
|
@ -473,15 +473,20 @@ 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;
|
||||
|
||||
_enter("");
|
||||
|
||||
crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
|
||||
&data_offset, &data_len);
|
||||
if (crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
|
||||
&data_offset, &data_len) < 0) {
|
||||
ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
|
||||
rxgk_abort_1_short_header);
|
||||
goto put_gk;
|
||||
}
|
||||
|
||||
hdr = kzalloc_obj(*hdr, GFP_NOFS);
|
||||
if (!hdr)
|
||||
|
|
@ -496,16 +501,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 +526,53 @@ 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);
|
||||
if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE,
|
||||
len, sizeof(*hdr)) < 0) {
|
||||
ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
|
||||
rxgk_abort_2_short_header);
|
||||
goto error;
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
|
|
@ -1076,11 +1084,12 @@ static int rxgk_sendmsg_respond_to_challenge(struct sk_buff *challenge,
|
|||
* unsigned int call_numbers<>;
|
||||
* };
|
||||
*/
|
||||
static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn,
|
||||
const struct krb5_enctype *krb5,
|
||||
struct sk_buff *skb,
|
||||
__be32 *p, __be32 *end)
|
||||
static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
|
||||
const struct krb5_enctype *krb5,
|
||||
struct sk_buff *skb,
|
||||
void *auth, unsigned int auth_len)
|
||||
{
|
||||
__be32 *p = auth, *end = auth + auth_len;
|
||||
u32 app_len, call_count, level, epoch, cid, i;
|
||||
|
||||
_enter("");
|
||||
|
|
@ -1143,37 +1152,6 @@ static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the authenticator and verify it.
|
||||
*/
|
||||
static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
|
||||
const struct krb5_enctype *krb5,
|
||||
struct sk_buff *skb,
|
||||
unsigned int auth_offset, unsigned int auth_len)
|
||||
{
|
||||
void *auth;
|
||||
__be32 *p;
|
||||
int ret;
|
||||
|
||||
auth = kmalloc(auth_len, GFP_NOFS);
|
||||
if (!auth)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = skb_copy_bits(skb, auth_offset, auth, auth_len);
|
||||
if (ret < 0) {
|
||||
ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
|
||||
rxgk_abort_resp_short_auth);
|
||||
goto error;
|
||||
}
|
||||
|
||||
p = auth;
|
||||
ret = rxgk_do_verify_authenticator(conn, krb5, skb, p,
|
||||
p + auth_len / sizeof(*p));
|
||||
error:
|
||||
kfree(auth);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify a response.
|
||||
*
|
||||
|
|
@ -1184,49 +1162,45 @@ static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
|
|||
* };
|
||||
*/
|
||||
static int rxgk_verify_response(struct rxrpc_connection *conn,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
void *buffer, unsigned int len)
|
||||
{
|
||||
const struct krb5_enctype *krb5;
|
||||
struct rxrpc_key_token *token;
|
||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||
struct rxgk_response rhdr;
|
||||
struct rxgk_response *rhdr;
|
||||
struct rxgk_context *gk;
|
||||
struct key *key = NULL;
|
||||
unsigned int offset = sizeof(struct rxrpc_wire_header);
|
||||
unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
|
||||
unsigned int token_offset, token_len;
|
||||
unsigned int auth_offset, auth_len;
|
||||
unsigned int resp_token_len, auth_len;
|
||||
void *resp_token, *auth;
|
||||
__be32 xauth_len;
|
||||
int ret, ec;
|
||||
|
||||
_enter("{%d}", conn->debug_id);
|
||||
|
||||
/* Parse the RXGK_Response object */
|
||||
if (sizeof(rhdr) + sizeof(__be32) > len)
|
||||
if (len < sizeof(*rhdr) + sizeof(__be32))
|
||||
goto short_packet;
|
||||
rhdr = buffer;
|
||||
buffer += sizeof(*rhdr);
|
||||
len -= sizeof(*rhdr);
|
||||
|
||||
resp_token = buffer;
|
||||
resp_token_len = ntohl(rhdr->token_len);
|
||||
if (resp_token_len > len ||
|
||||
xdr_round_up(resp_token_len) + sizeof(__be32) > len)
|
||||
goto short_packet;
|
||||
|
||||
if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0)
|
||||
goto short_packet;
|
||||
offset += sizeof(rhdr);
|
||||
len -= sizeof(rhdr);
|
||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, resp_token_len);
|
||||
|
||||
token_offset = offset;
|
||||
token_len = ntohl(rhdr.token_len);
|
||||
if (token_len > len ||
|
||||
xdr_round_up(token_len) + sizeof(__be32) > len)
|
||||
goto short_packet;
|
||||
buffer += xdr_round_up(resp_token_len);
|
||||
len -= xdr_round_up(resp_token_len);
|
||||
|
||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, token_len);
|
||||
|
||||
offset += xdr_round_up(token_len);
|
||||
len -= xdr_round_up(token_len);
|
||||
|
||||
if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0)
|
||||
goto short_packet;
|
||||
offset += sizeof(xauth_len);
|
||||
xauth_len = *(__be32 *)buffer;
|
||||
buffer += sizeof(xauth_len);
|
||||
len -= sizeof(xauth_len);
|
||||
|
||||
auth_offset = offset;
|
||||
auth = buffer;
|
||||
auth_len = ntohl(xauth_len);
|
||||
if (auth_len > len)
|
||||
goto short_packet;
|
||||
|
|
@ -1241,7 +1215,7 @@ static int rxgk_verify_response(struct rxrpc_connection *conn,
|
|||
* to the app to deal with - which might mean a round trip to
|
||||
* userspace.
|
||||
*/
|
||||
ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key);
|
||||
ret = rxgk_extract_token(conn, skb, resp_token, resp_token_len, &key);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
|
|
@ -1255,7 +1229,7 @@ static int rxgk_verify_response(struct rxrpc_connection *conn,
|
|||
*/
|
||||
token = key->payload.data[0];
|
||||
conn->security_level = token->rxgk->level;
|
||||
conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time);
|
||||
conn->rxgk.start_time = __be64_to_cpu(rhdr->start_time);
|
||||
|
||||
gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS);
|
||||
if (IS_ERR(gk)) {
|
||||
|
|
@ -1265,18 +1239,18 @@ static int rxgk_verify_response(struct rxrpc_connection *conn,
|
|||
|
||||
krb5 = gk->krb5;
|
||||
|
||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, krb5->etype, sp->hdr.cksum, token_len);
|
||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, krb5->etype, sp->hdr.cksum,
|
||||
resp_token_len);
|
||||
|
||||
/* Decrypt, parse and verify the authenticator. */
|
||||
ret = rxgk_decrypt_skb(krb5, gk->resp_enc, skb,
|
||||
&auth_offset, &auth_len, &ec);
|
||||
ret = rxgk_decrypt(krb5, gk->resp_enc, &auth, &auth_len, &ec);
|
||||
if (ret < 0) {
|
||||
rxrpc_abort_conn(conn, skb, RXGK_SEALEDINCON, ret,
|
||||
rxgk_abort_resp_auth_dec);
|
||||
goto out_gk;
|
||||
}
|
||||
|
||||
ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len);
|
||||
ret = rxgk_verify_authenticator(conn, krb5, skb, auth, auth_len);
|
||||
if (ret < 0)
|
||||
goto out_gk;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
* };
|
||||
*/
|
||||
int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||
unsigned int ticket_offset, unsigned int ticket_len,
|
||||
void *buffer, unsigned int ticket_len,
|
||||
struct key **_key)
|
||||
{
|
||||
struct rxrpc_key_token *token;
|
||||
|
|
@ -49,7 +49,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
size_t pre_ticket_len, payload_len;
|
||||
unsigned int klen, enctype;
|
||||
void *payload, *ticket;
|
||||
__be32 *t, *p, *q, tmp[2];
|
||||
__be32 *t, *p, *q, *tmp;
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
|
|
@ -59,10 +59,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
rxgk_abort_resp_short_yfs_tkt);
|
||||
|
||||
/* Get the session key length */
|
||||
ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp));
|
||||
if (ret < 0)
|
||||
return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
|
||||
rxgk_abort_resp_short_yfs_klen);
|
||||
tmp = buffer;
|
||||
enctype = ntohl(tmp[0]);
|
||||
klen = ntohl(tmp[1]);
|
||||
|
||||
|
|
@ -84,12 +81,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
* it.
|
||||
*/
|
||||
ticket = payload + pre_ticket_len;
|
||||
ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len);
|
||||
if (ret < 0) {
|
||||
ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
|
||||
rxgk_abort_resp_short_yfs_tkt);
|
||||
goto error;
|
||||
}
|
||||
memcpy(ticket, buffer, ticket_len);
|
||||
|
||||
/* Fill out the form header. */
|
||||
p = payload;
|
||||
|
|
@ -131,7 +123,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* Ticket read in with skb_copy_bits above */
|
||||
/* Ticket appended above. */
|
||||
q += xdr_round_up(ticket_len) / 4;
|
||||
if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
|
||||
ret = -EIO;
|
||||
|
|
@ -182,14 +174,15 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
* [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1]
|
||||
*/
|
||||
int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||
unsigned int token_offset, unsigned int token_len,
|
||||
void *token, unsigned int token_len,
|
||||
struct key **_key)
|
||||
{
|
||||
const struct krb5_enctype *krb5;
|
||||
const struct krb5_buffer *server_secret;
|
||||
struct crypto_aead *token_enc = NULL;
|
||||
struct key *server_key;
|
||||
unsigned int ticket_offset, ticket_len;
|
||||
unsigned int ticket_len;
|
||||
void *ticket;
|
||||
u32 kvno, enctype;
|
||||
int ret, ec = 0;
|
||||
|
||||
|
|
@ -197,24 +190,23 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
__be32 kvno;
|
||||
__be32 enctype;
|
||||
__be32 token_len;
|
||||
} container;
|
||||
} *container;
|
||||
|
||||
if (token_len < sizeof(container))
|
||||
if (token_len < sizeof(*container))
|
||||
goto short_packet;
|
||||
|
||||
/* Decode the RXGK_TokenContainer object. This tells us which server
|
||||
* key we should be using. We can then fetch the key, get the secret
|
||||
* and set up the crypto to extract the token.
|
||||
*/
|
||||
if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0)
|
||||
goto short_packet;
|
||||
container = token;
|
||||
token += sizeof(*container);
|
||||
|
||||
kvno = ntohl(container.kvno);
|
||||
enctype = ntohl(container.enctype);
|
||||
ticket_len = ntohl(container.token_len);
|
||||
ticket_offset = token_offset + sizeof(container);
|
||||
kvno = ntohl(container->kvno);
|
||||
enctype = ntohl(container->enctype);
|
||||
ticket_len = ntohl(container->token_len);
|
||||
|
||||
if (ticket_len > xdr_round_down(token_len - sizeof(container)))
|
||||
if (ticket_len > xdr_round_down(token_len - sizeof(*container)))
|
||||
goto short_packet;
|
||||
|
||||
_debug("KVNO %u", kvno);
|
||||
|
|
@ -237,8 +229,8 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
* gain access to K0, from which we can derive the transport key and
|
||||
* thence decode the authenticator.
|
||||
*/
|
||||
ret = rxgk_decrypt_skb(krb5, token_enc, skb,
|
||||
&ticket_offset, &ticket_len, &ec);
|
||||
ticket = token;
|
||||
ret = rxgk_decrypt(krb5, token_enc, &ticket, &ticket_len, &ec);
|
||||
crypto_free_aead(token_enc);
|
||||
token_enc = NULL;
|
||||
if (ret < 0) {
|
||||
|
|
@ -248,7 +240,7 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = conn->security->default_decode_ticket(conn, skb, ticket_offset,
|
||||
ret = conn->security->default_decode_ticket(conn, skb, ticket,
|
||||
ticket_len, _key);
|
||||
if (ret < 0)
|
||||
goto cant_get_token;
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ struct rxgk_context {
|
|||
* rxgk_app.c
|
||||
*/
|
||||
int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||
unsigned int ticket_offset, unsigned int ticket_len,
|
||||
void *ticket, unsigned int ticket_len,
|
||||
struct key **_key);
|
||||
int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||
unsigned int token_offset, unsigned int token_len,
|
||||
void *token, unsigned int token_len,
|
||||
struct key **_key);
|
||||
|
||||
/*
|
||||
|
|
@ -62,31 +62,30 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
|
|||
gfp_t gfp);
|
||||
|
||||
/*
|
||||
* Apply decryption and checksumming functions to part of an skbuff. The
|
||||
* offset and length are updated to reflect the actual content of the encrypted
|
||||
* 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_skb(const struct krb5_enctype *krb5,
|
||||
struct crypto_aead *aead,
|
||||
struct sk_buff *skb,
|
||||
unsigned int *_offset, unsigned int *_len,
|
||||
int *_error_code)
|
||||
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[16];
|
||||
struct scatterlist sg[1];
|
||||
size_t offset = 0, len = *_len;
|
||||
int nr_sg, ret;
|
||||
int ret;
|
||||
|
||||
sg_init_table(sg, ARRAY_SIZE(sg));
|
||||
nr_sg = skb_to_sgvec(skb, sg, *_offset, len);
|
||||
if (unlikely(nr_sg < 0))
|
||||
return nr_sg;
|
||||
sg_init_one(sg, *_data, len);
|
||||
|
||||
ret = crypto_krb5_decrypt(krb5, aead, sg, nr_sg,
|
||||
&offset, &len);
|
||||
ret = crypto_krb5_decrypt(krb5, aead, sg, 1, &offset, &len);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
*_offset += offset;
|
||||
if (offset & 3) {
|
||||
*_error_code = RXGK_INCONSISTENCY;
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
}
|
||||
*_data += offset;
|
||||
*_len = len;
|
||||
break;
|
||||
case -EBADMSG: /* Checksum mismatch. */
|
||||
|
|
@ -106,31 +105,26 @@ int rxgk_decrypt_skb(const struct krb5_enctype *krb5,
|
|||
}
|
||||
|
||||
/*
|
||||
* Check the MIC on a region of an skbuff. The offset and length are updated
|
||||
* to reflect the actual content of the secure region.
|
||||
* 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_skb(const struct krb5_enctype *krb5,
|
||||
struct crypto_shash *shash,
|
||||
const struct krb5_buffer *metadata,
|
||||
struct sk_buff *skb,
|
||||
unsigned int *_offset, unsigned int *_len,
|
||||
u32 *_error_code)
|
||||
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[16];
|
||||
struct scatterlist sg[1];
|
||||
size_t offset = 0, len = *_len;
|
||||
int nr_sg, ret;
|
||||
int ret;
|
||||
|
||||
sg_init_table(sg, ARRAY_SIZE(sg));
|
||||
nr_sg = skb_to_sgvec(skb, sg, *_offset, len);
|
||||
if (unlikely(nr_sg < 0))
|
||||
return nr_sg;
|
||||
sg_init_one(sg, *_data, len);
|
||||
|
||||
ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg,
|
||||
&offset, &len);
|
||||
ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, 1, &offset, &len);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
*_offset += offset;
|
||||
*_data += offset;
|
||||
*_len = len;
|
||||
break;
|
||||
case -EBADMSG: /* Checksum mismatch */
|
||||
|
|
|
|||
|
|
@ -430,27 +430,25 @@ 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));
|
||||
|
|
@ -464,13 +462,11 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
|
|||
return ret;
|
||||
|
||||
/* 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;
|
||||
|
|
@ -479,10 +475,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;
|
||||
|
|
@ -496,43 +492,28 @@ 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;
|
||||
int 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);
|
||||
|
||||
/* Don't let the crypto algo see a misaligned length. */
|
||||
sp->len = round_down(sp->len, 8);
|
||||
len = round_down(len, 8);
|
||||
|
||||
/* 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_objs(*sg, nsg, 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];
|
||||
|
|
@ -540,11 +521,9 @@ 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);
|
||||
ret = crypto_skcipher_decrypt(req);
|
||||
skcipher_request_zero(req);
|
||||
if (sg != _sg)
|
||||
kfree(sg);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM)
|
||||
return ret;
|
||||
|
|
@ -553,13 +532,11 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
|
|||
}
|
||||
|
||||
/* 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;
|
||||
|
|
@ -569,17 +546,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)
|
||||
{
|
||||
|
|
@ -985,7 +963,6 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
|
|||
*_expiry = 0;
|
||||
|
||||
ASSERT(server_key->payload.data[0] != NULL);
|
||||
ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
|
||||
|
||||
memcpy(&iv, &server_key->payload.data[2], sizeof(iv));
|
||||
|
||||
|
|
@ -1134,14 +1111,15 @@ static int rxkad_decrypt_response(struct rxrpc_connection *conn,
|
|||
* verify a response
|
||||
*/
|
||||
static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
void *buffer, unsigned int len)
|
||||
{
|
||||
struct rxkad_response *response;
|
||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||
struct rxrpc_crypt session_key;
|
||||
struct key *server_key;
|
||||
time64_t expiry;
|
||||
void *ticket = NULL;
|
||||
void *ticket;
|
||||
u32 version, kvno, ticket_len, level;
|
||||
__be32 csum;
|
||||
int ret, i;
|
||||
|
|
@ -1164,13 +1142,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
|||
}
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
response = kzalloc_obj(struct rxkad_response, GFP_NOFS);
|
||||
if (!response)
|
||||
goto error;
|
||||
|
||||
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
|
||||
response, sizeof(*response)) < 0) {
|
||||
response = buffer;
|
||||
if (len < sizeof(*response)) {
|
||||
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
|
||||
rxkad_abort_resp_short);
|
||||
goto error;
|
||||
|
|
@ -1182,6 +1155,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
|||
|
||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);
|
||||
|
||||
buffer += sizeof(*response);
|
||||
len -= sizeof(*response);
|
||||
|
||||
if (version != RXKAD_VERSION) {
|
||||
ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
|
||||
rxkad_abort_resp_version);
|
||||
|
|
@ -1201,13 +1177,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
|||
}
|
||||
|
||||
/* extract the kerberos ticket and decrypt and decode it */
|
||||
ret = -ENOMEM;
|
||||
ticket = kmalloc(ticket_len, GFP_NOFS);
|
||||
if (!ticket)
|
||||
goto error;
|
||||
|
||||
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
|
||||
ticket, ticket_len) < 0) {
|
||||
ticket = buffer;
|
||||
if (ticket_len > len) {
|
||||
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
|
||||
rxkad_abort_resp_short_tkt);
|
||||
goto error;
|
||||
|
|
@ -1287,8 +1258,6 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
|||
ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
|
||||
|
||||
error:
|
||||
kfree(ticket);
|
||||
kfree(response);
|
||||
key_put(server_key);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user