rxrpc: Fix RESPONSE packet verification to extract skb to a linear buffer
This improves the fix for CVE-2026-43500.
Fix the verification of RESPONSE packets to avoid the problem of
overwriting a RESPONSE packet sent via splice to a local address by
extracting the contents of the UDP packet into a kmalloc'd linear buffer
rather than decrypting the data in place in the sk_buff (which may corrupt
the original buffer).
Fixes: 24481a7f57 ("rxrpc: Fix conn-level packet handling to unshare RESPONSE packets")
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
cc: stable@kernel.org
Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
Tested-by: Marc Dionne <marc.dionne@auristor.com>
Link: https://patch.msgid.link/20260515230516.2718212-4-dhowells@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
master
parent
d2bc90cf6c
commit
8bfab4b6ff
|
|
@ -307,15 +307,16 @@ struct rxrpc_security {
|
||||||
struct sk_buff *challenge);
|
struct sk_buff *challenge);
|
||||||
|
|
||||||
/* verify a response */
|
/* verify a response */
|
||||||
int (*verify_response)(struct rxrpc_connection *,
|
int (*verify_response)(struct rxrpc_connection *conn,
|
||||||
struct sk_buff *);
|
struct sk_buff *response_skb,
|
||||||
|
void *response, unsigned int len);
|
||||||
|
|
||||||
/* clear connection security */
|
/* clear connection security */
|
||||||
void (*clear)(struct rxrpc_connection *);
|
void (*clear)(struct rxrpc_connection *);
|
||||||
|
|
||||||
/* Default ticket -> key decoder */
|
/* Default ticket -> key decoder */
|
||||||
int (*default_decode_ticket)(struct rxrpc_connection *conn, struct sk_buff *skb,
|
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);
|
struct key **_key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -243,28 +243,22 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call)
|
||||||
static int rxrpc_verify_response(struct rxrpc_connection *conn,
|
static int rxrpc_verify_response(struct rxrpc_connection *conn,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
|
||||||
|
void *buffer;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (skb_cloned(skb) || skb_has_frag_list(skb) ||
|
buffer = kmalloc(len, GFP_NOFS);
|
||||||
skb_has_shared_frag(skb)) {
|
if (!buffer)
|
||||||
/* Copy the packet if shared so that we can do in-place
|
return -ENOMEM;
|
||||||
* decryption.
|
|
||||||
*/
|
|
||||||
struct sk_buff *nskb = skb_copy(skb, GFP_NOFS);
|
|
||||||
|
|
||||||
if (nskb) {
|
ret = skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), buffer, len);
|
||||||
rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
|
if (ret < 0)
|
||||||
ret = conn->security->verify_response(conn, nskb);
|
goto out;
|
||||||
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 = conn->security->verify_response(conn, skb, buffer, len);
|
||||||
|
|
||||||
|
out:
|
||||||
|
kfree(buffer);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,10 @@ static int none_sendmsg_respond_to_challenge(struct sk_buff *challenge,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int none_verify_response(struct rxrpc_connection *conn,
|
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);
|
rxrpc_eproto_rxnull_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1084,11 +1084,12 @@ static int rxgk_sendmsg_respond_to_challenge(struct sk_buff *challenge,
|
||||||
* unsigned int call_numbers<>;
|
* unsigned int call_numbers<>;
|
||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn,
|
static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
|
||||||
const struct krb5_enctype *krb5,
|
const struct krb5_enctype *krb5,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
__be32 *p, __be32 *end)
|
void *auth, unsigned int auth_len)
|
||||||
{
|
{
|
||||||
|
__be32 *p = auth, *end = auth + auth_len;
|
||||||
u32 app_len, call_count, level, epoch, cid, i;
|
u32 app_len, call_count, level, epoch, cid, i;
|
||||||
|
|
||||||
_enter("");
|
_enter("");
|
||||||
|
|
@ -1151,37 +1152,6 @@ static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn,
|
||||||
return 0;
|
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.
|
* Verify a response.
|
||||||
*
|
*
|
||||||
|
|
@ -1192,49 +1162,45 @@ error:
|
||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
static int rxgk_verify_response(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;
|
const struct krb5_enctype *krb5;
|
||||||
struct rxrpc_key_token *token;
|
struct rxrpc_key_token *token;
|
||||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||||
struct rxgk_response rhdr;
|
struct rxgk_response *rhdr;
|
||||||
struct rxgk_context *gk;
|
struct rxgk_context *gk;
|
||||||
struct key *key = NULL;
|
struct key *key = NULL;
|
||||||
unsigned int offset = sizeof(struct rxrpc_wire_header);
|
unsigned int resp_token_len, auth_len;
|
||||||
unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
|
void *resp_token, *auth;
|
||||||
unsigned int token_offset, token_len;
|
|
||||||
unsigned int auth_offset, auth_len;
|
|
||||||
__be32 xauth_len;
|
__be32 xauth_len;
|
||||||
int ret, ec;
|
int ret, ec;
|
||||||
|
|
||||||
_enter("{%d}", conn->debug_id);
|
_enter("{%d}", conn->debug_id);
|
||||||
|
|
||||||
/* Parse the RXGK_Response object */
|
/* 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;
|
goto short_packet;
|
||||||
|
|
||||||
if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0)
|
trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, resp_token_len);
|
||||||
goto short_packet;
|
|
||||||
offset += sizeof(rhdr);
|
|
||||||
len -= sizeof(rhdr);
|
|
||||||
|
|
||||||
token_offset = offset;
|
buffer += xdr_round_up(resp_token_len);
|
||||||
token_len = ntohl(rhdr.token_len);
|
len -= xdr_round_up(resp_token_len);
|
||||||
if (token_len > len ||
|
|
||||||
xdr_round_up(token_len) + sizeof(__be32) > len)
|
|
||||||
goto short_packet;
|
|
||||||
|
|
||||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, token_len);
|
xauth_len = *(__be32 *)buffer;
|
||||||
|
buffer += sizeof(xauth_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);
|
|
||||||
len -= sizeof(xauth_len);
|
len -= sizeof(xauth_len);
|
||||||
|
|
||||||
auth_offset = offset;
|
auth = buffer;
|
||||||
auth_len = ntohl(xauth_len);
|
auth_len = ntohl(xauth_len);
|
||||||
if (auth_len > len)
|
if (auth_len > len)
|
||||||
goto short_packet;
|
goto short_packet;
|
||||||
|
|
@ -1249,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
|
* to the app to deal with - which might mean a round trip to
|
||||||
* userspace.
|
* 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)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
@ -1263,7 +1229,7 @@ static int rxgk_verify_response(struct rxrpc_connection *conn,
|
||||||
*/
|
*/
|
||||||
token = key->payload.data[0];
|
token = key->payload.data[0];
|
||||||
conn->security_level = token->rxgk->level;
|
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);
|
gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS);
|
||||||
if (IS_ERR(gk)) {
|
if (IS_ERR(gk)) {
|
||||||
|
|
@ -1273,18 +1239,18 @@ static int rxgk_verify_response(struct rxrpc_connection *conn,
|
||||||
|
|
||||||
krb5 = gk->krb5;
|
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. */
|
/* Decrypt, parse and verify the authenticator. */
|
||||||
ret = rxgk_decrypt_skb(krb5, gk->resp_enc, skb,
|
ret = rxgk_decrypt(krb5, gk->resp_enc, &auth, &auth_len, &ec);
|
||||||
&auth_offset, &auth_len, &ec);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rxrpc_abort_conn(conn, skb, RXGK_SEALEDINCON, ret,
|
rxrpc_abort_conn(conn, skb, RXGK_SEALEDINCON, ret,
|
||||||
rxgk_abort_resp_auth_dec);
|
rxgk_abort_resp_auth_dec);
|
||||||
goto out_gk;
|
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)
|
if (ret < 0)
|
||||||
goto out_gk;
|
goto out_gk;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
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 key **_key)
|
||||||
{
|
{
|
||||||
struct rxrpc_key_token *token;
|
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;
|
size_t pre_ticket_len, payload_len;
|
||||||
unsigned int klen, enctype;
|
unsigned int klen, enctype;
|
||||||
void *payload, *ticket;
|
void *payload, *ticket;
|
||||||
__be32 *t, *p, *q, tmp[2];
|
__be32 *t, *p, *q, *tmp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
_enter("");
|
_enter("");
|
||||||
|
|
@ -59,10 +59,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
rxgk_abort_resp_short_yfs_tkt);
|
rxgk_abort_resp_short_yfs_tkt);
|
||||||
|
|
||||||
/* Get the session key length */
|
/* Get the session key length */
|
||||||
ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp));
|
tmp = buffer;
|
||||||
if (ret < 0)
|
|
||||||
return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
|
|
||||||
rxgk_abort_resp_short_yfs_klen);
|
|
||||||
enctype = ntohl(tmp[0]);
|
enctype = ntohl(tmp[0]);
|
||||||
klen = ntohl(tmp[1]);
|
klen = ntohl(tmp[1]);
|
||||||
|
|
||||||
|
|
@ -84,12 +81,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
* it.
|
* it.
|
||||||
*/
|
*/
|
||||||
ticket = payload + pre_ticket_len;
|
ticket = payload + pre_ticket_len;
|
||||||
ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len);
|
memcpy(ticket, buffer, ticket_len);
|
||||||
if (ret < 0) {
|
|
||||||
ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
|
|
||||||
rxgk_abort_resp_short_yfs_tkt);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill out the form header. */
|
/* Fill out the form header. */
|
||||||
p = payload;
|
p = payload;
|
||||||
|
|
@ -131,7 +123,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ticket read in with skb_copy_bits above */
|
/* Ticket appended above. */
|
||||||
q += xdr_round_up(ticket_len) / 4;
|
q += xdr_round_up(ticket_len) / 4;
|
||||||
if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
|
if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
|
@ -182,14 +174,15 @@ error:
|
||||||
* [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1]
|
* [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,
|
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)
|
struct key **_key)
|
||||||
{
|
{
|
||||||
const struct krb5_enctype *krb5;
|
const struct krb5_enctype *krb5;
|
||||||
const struct krb5_buffer *server_secret;
|
const struct krb5_buffer *server_secret;
|
||||||
struct crypto_aead *token_enc = NULL;
|
struct crypto_aead *token_enc = NULL;
|
||||||
struct key *server_key;
|
struct key *server_key;
|
||||||
unsigned int ticket_offset, ticket_len;
|
unsigned int ticket_len;
|
||||||
|
void *ticket;
|
||||||
u32 kvno, enctype;
|
u32 kvno, enctype;
|
||||||
int ret, ec = 0;
|
int ret, ec = 0;
|
||||||
|
|
||||||
|
|
@ -197,24 +190,23 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
__be32 kvno;
|
__be32 kvno;
|
||||||
__be32 enctype;
|
__be32 enctype;
|
||||||
__be32 token_len;
|
__be32 token_len;
|
||||||
} container;
|
} *container;
|
||||||
|
|
||||||
if (token_len < sizeof(container))
|
if (token_len < sizeof(*container))
|
||||||
goto short_packet;
|
goto short_packet;
|
||||||
|
|
||||||
/* Decode the RXGK_TokenContainer object. This tells us which server
|
/* Decode the RXGK_TokenContainer object. This tells us which server
|
||||||
* key we should be using. We can then fetch the key, get the secret
|
* key we should be using. We can then fetch the key, get the secret
|
||||||
* and set up the crypto to extract the token.
|
* and set up the crypto to extract the token.
|
||||||
*/
|
*/
|
||||||
if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0)
|
container = token;
|
||||||
goto short_packet;
|
token += sizeof(*container);
|
||||||
|
|
||||||
kvno = ntohl(container.kvno);
|
kvno = ntohl(container->kvno);
|
||||||
enctype = ntohl(container.enctype);
|
enctype = ntohl(container->enctype);
|
||||||
ticket_len = ntohl(container.token_len);
|
ticket_len = ntohl(container->token_len);
|
||||||
ticket_offset = token_offset + sizeof(container);
|
|
||||||
|
|
||||||
if (ticket_len > xdr_round_down(token_len - sizeof(container)))
|
if (ticket_len > xdr_round_down(token_len - sizeof(*container)))
|
||||||
goto short_packet;
|
goto short_packet;
|
||||||
|
|
||||||
_debug("KVNO %u", kvno);
|
_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
|
* gain access to K0, from which we can derive the transport key and
|
||||||
* thence decode the authenticator.
|
* thence decode the authenticator.
|
||||||
*/
|
*/
|
||||||
ret = rxgk_decrypt_skb(krb5, token_enc, skb,
|
ticket = token;
|
||||||
&ticket_offset, &ticket_len, &ec);
|
ret = rxgk_decrypt(krb5, token_enc, &ticket, &ticket_len, &ec);
|
||||||
crypto_free_aead(token_enc);
|
crypto_free_aead(token_enc);
|
||||||
token_enc = NULL;
|
token_enc = NULL;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
@ -248,7 +240,7 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = conn->security->default_decode_ticket(conn, skb, ticket_offset,
|
ret = conn->security->default_decode_ticket(conn, skb, ticket,
|
||||||
ticket_len, _key);
|
ticket_len, _key);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto cant_get_token;
|
goto cant_get_token;
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,10 @@ struct rxgk_context {
|
||||||
* rxgk_app.c
|
* rxgk_app.c
|
||||||
*/
|
*/
|
||||||
int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
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);
|
struct key **_key);
|
||||||
int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
|
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);
|
struct key **_key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -61,50 +61,6 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
|
||||||
const struct krb5_enctype **_krb5,
|
const struct krb5_enctype **_krb5,
|
||||||
gfp_t gfp);
|
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
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
struct scatterlist sg[16];
|
|
||||||
size_t offset = 0, len = *_len;
|
|
||||||
int nr_sg, 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;
|
|
||||||
|
|
||||||
ret = crypto_krb5_decrypt(krb5, aead, sg, nr_sg,
|
|
||||||
&offset, &len);
|
|
||||||
switch (ret) {
|
|
||||||
case 0:
|
|
||||||
*_offset += 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply decryption and checksumming functions a flat data buffer. The data
|
* Apply decryption and checksumming functions a flat data buffer. The data
|
||||||
* point and length are updated to reflect the actual content of the encrypted
|
* point and length are updated to reflect the actual content of the encrypted
|
||||||
|
|
@ -148,50 +104,6 @@ static inline int rxgk_decrypt(const struct krb5_enctype *krb5,
|
||||||
return ret;
|
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.
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
struct scatterlist sg[16];
|
|
||||||
size_t offset = 0, len = *_len;
|
|
||||||
int nr_sg, 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;
|
|
||||||
|
|
||||||
ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg,
|
|
||||||
&offset, &len);
|
|
||||||
switch (ret) {
|
|
||||||
case 0:
|
|
||||||
*_offset += 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 flat buffer. The data pointer and length are updated to
|
* Check the MIC on a flat buffer. The data pointer and length are updated to
|
||||||
* reflect the actual content of the secure region.
|
* reflect the actual content of the secure region.
|
||||||
|
|
|
||||||
|
|
@ -963,7 +963,6 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
|
||||||
*_expiry = 0;
|
*_expiry = 0;
|
||||||
|
|
||||||
ASSERT(server_key->payload.data[0] != NULL);
|
ASSERT(server_key->payload.data[0] != NULL);
|
||||||
ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
|
|
||||||
|
|
||||||
memcpy(&iv, &server_key->payload.data[2], sizeof(iv));
|
memcpy(&iv, &server_key->payload.data[2], sizeof(iv));
|
||||||
|
|
||||||
|
|
@ -1112,14 +1111,15 @@ unlock:
|
||||||
* verify a response
|
* verify a response
|
||||||
*/
|
*/
|
||||||
static int rxkad_verify_response(struct rxrpc_connection *conn,
|
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 rxkad_response *response;
|
||||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||||
struct rxrpc_crypt session_key;
|
struct rxrpc_crypt session_key;
|
||||||
struct key *server_key;
|
struct key *server_key;
|
||||||
time64_t expiry;
|
time64_t expiry;
|
||||||
void *ticket = NULL;
|
void *ticket;
|
||||||
u32 version, kvno, ticket_len, level;
|
u32 version, kvno, ticket_len, level;
|
||||||
__be32 csum;
|
__be32 csum;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
@ -1142,13 +1142,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = -ENOMEM;
|
response = buffer;
|
||||||
response = kzalloc_obj(struct rxkad_response, GFP_NOFS);
|
if (len < sizeof(*response)) {
|
||||||
if (!response)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
|
|
||||||
response, sizeof(*response)) < 0) {
|
|
||||||
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
|
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
|
||||||
rxkad_abort_resp_short);
|
rxkad_abort_resp_short);
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -1160,6 +1155,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
|
|
||||||
trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);
|
trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);
|
||||||
|
|
||||||
|
buffer += sizeof(*response);
|
||||||
|
len -= sizeof(*response);
|
||||||
|
|
||||||
if (version != RXKAD_VERSION) {
|
if (version != RXKAD_VERSION) {
|
||||||
ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
|
ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
|
||||||
rxkad_abort_resp_version);
|
rxkad_abort_resp_version);
|
||||||
|
|
@ -1179,13 +1177,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extract the kerberos ticket and decrypt and decode it */
|
/* extract the kerberos ticket and decrypt and decode it */
|
||||||
ret = -ENOMEM;
|
ticket = buffer;
|
||||||
ticket = kmalloc(ticket_len, GFP_NOFS);
|
if (ticket_len > len) {
|
||||||
if (!ticket)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
|
|
||||||
ticket, ticket_len) < 0) {
|
|
||||||
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
|
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
|
||||||
rxkad_abort_resp_short_tkt);
|
rxkad_abort_resp_short_tkt);
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -1265,8 +1258,6 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
|
ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
kfree(ticket);
|
|
||||||
kfree(response);
|
|
||||||
key_put(server_key);
|
key_put(server_key);
|
||||||
_leave(" = %d", ret);
|
_leave(" = %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue