// SPDX-License-Identifier: GPL-2.0-or-later /* GSSAPI-based RxRPC security * * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Parse the information from a server key
*/ staticint rxgk_preparse_server_key(struct key_preparsed_payload *prep)
{ conststruct krb5_enctype *krb5; struct krb5_buffer *server_key = (void *)&prep->payload.data[2]; unsignedint service, sec_class, kvno, enctype; int n = 0;
/* * Handle rekeying the connection when we see our limits overrun or when the * far side decided to rekey. * * Returns a ref on the context if successful or -ESTALE if the key is out of * date.
*/ staticstruct rxgk_context *rxgk_rekey(struct rxrpc_connection *conn, const u16 *specific_key_number)
{ struct rxgk_context *gk, *dead = NULL; unsignedint key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1; bool crank = false;
/* * Get the specified keying context. * * Returns a ref on the context if successful or -ESTALE if the key is out of * date.
*/ staticstruct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn, const u16 *specific_key_number)
{ struct rxgk_context *gk; unsignedint key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
current_key = conn->rxgk.key_number; if (!specific_key_number) {
key_number = current_key;
} else { /* Only the bottom 16 bits of the key number are exposed in the * header, so we try and keep the upper 16 bits in step. The * whole 32 bits are used to generate the TK.
*/ if (*specific_key_number == (u16)current_key)
key_number = current_key; elseif (*specific_key_number == (u16)(current_key - 1))
key_number = current_key - 1; elseif (*specific_key_number == (u16)(current_key + 1)) goto rekey; else goto bad_key;
}
gk = conn->rxgk.keys[key_number & mask]; if (!gk) goto slow_path; if (!specific_key_number &&
key_number < UINT_MAX) { if (time_after(jiffies, gk->expiry) ||
gk->bytes_remaining < 0) {
set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags); goto slow_path;
}
if (test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags)) goto slow_path;
}
switch (conn->security_level) { case RXRPC_SECURITY_PLAIN: case RXRPC_SECURITY_AUTH: case RXRPC_SECURITY_ENCRYPT: break; default:
ret = -EKEYREJECTED; goto error;
}
/* * Clean up the crypto on a call.
*/ staticvoid rxgk_free_call_crypto(struct rxrpc_call *call)
{
}
/* * Work out how much data we can put in a packet.
*/ staticstruct rxrpc_txbuf *rxgk_alloc_txbuf(struct rxrpc_call *call, size_t remain, gfp_t gfp)
{ enum krb5_crypto_mode mode; struct rxgk_context *gk; struct rxrpc_txbuf *txb;
size_t shdr, alloc, limit, part, offset, gap;
gk = rxgk_get_key(call->conn, NULL); if (IS_ERR(gk)) return NULL;
/* Work out the maximum amount of data that will fit. */
alloc = RXRPC_JUMBO_DATALEN;
limit = crypto_krb5_how_much_data(gk->krb5, mode, &alloc, &offset);
if (remain < limit - shdr) {
part = remain;
alloc = crypto_krb5_how_much_buffer(gk->krb5, mode,
shdr + part, &offset);
gap = 0;
} else {
part = limit - shdr;
gap = RXRPC_JUMBO_DATALEN - alloc;
alloc = RXRPC_JUMBO_DATALEN;
}
rxgk_put(gk);
txb = rxrpc_alloc_data_txbuf(call, alloc, 16, gfp); if (!txb) return NULL;
ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); if (ret < 0) { if (ret != -ENOMEM)
rxrpc_abort_eproto(call, skb, ac, rxgk_abort_2_decrypt_eproto); goto error;
}
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);
/* * Verify the security on a received packet or subpacket (if part of a * jumbo packet).
*/ staticint rxgk_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
{ struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxgk_context *gk;
u16 key_number = sp->hdr.cksum;
call->security_enctype = gk->krb5->etype; switch (call->conn->security_level) { case RXRPC_SECURITY_PLAIN:
rxgk_put(gk); return 0; case RXRPC_SECURITY_AUTH: return rxgk_verify_packet_integrity(call, gk, skb); case RXRPC_SECURITY_ENCRYPT: return rxgk_verify_packet_encrypted(call, gk, skb); default:
rxgk_put(gk); return -ENOANO;
}
}
/* * Allocate memory to hold a challenge or a response packet. We're not running * in the io_thread, so we can't use ->tx_alloc.
*/ staticstruct page *rxgk_alloc_packet(size_t total_len)
{
gfp_t gfp = GFP_NOFS; int order;
order = get_order(total_len); if (order > 0)
gfp |= __GFP_COMP; return alloc_pages(gfp, order);
}
/* * Fill out the control message to pass to userspace to inform about the * challenge.
*/ staticint rxgk_challenge_to_recvmsg(struct rxrpc_connection *conn, struct sk_buff *challenge, struct msghdr *msg)
{ struct rxgk_challenge chall;
/* * Insert the requisite amount of XDR padding for the length given.
*/ staticint rxgk_pad_out(struct sk_buff *response, size_t len, size_t offset)
{
__be32 zero = 0;
size_t pad = xdr_round_up(len) - len; int ret;
if (!pad) return 0;
ret = skb_store_bits(response, offset, &zero, pad); if (ret < 0) return ret; return pad;
}
/* * Insert the header into the response.
*/ static noinline ssize_t rxgk_insert_response_header(struct rxrpc_connection *conn, struct rxgk_context *gk, struct sk_buff *response,
size_t offset)
{ struct rxrpc_skb_priv *rsp = rxrpc_skb(response);
/** * rxgk_kernel_respond_to_challenge - Respond to a challenge with appdata * @challenge: The challenge to respond to * @appdata: The application data to include in the RESPONSE authenticator * * Allow a kernel application to respond to a CHALLENGE with application data * to be included in the RxGK RESPONSE Authenticator. * * Return: %0 if successful and a negative error code otherwise.
*/ int rxgk_kernel_respond_to_challenge(struct sk_buff *challenge, struct krb5_buffer *appdata)
{ struct rxrpc_skb_priv *csp = rxrpc_skb(challenge);
/* * Parse sendmsg() control message and respond to challenge. We need to see if * there's an appdata to fish out.
*/ staticint rxgk_sendmsg_respond_to_challenge(struct sk_buff *challenge, struct msghdr *msg)
{ struct krb5_buffer appdata = {}; struct cmsghdr *cmsg;
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);
auth_offset = offset;
auth_len = ntohl(xauth_len); if (auth_len < len) goto short_packet; if (auth_len & 3) goto inconsistent; if (auth_len < 20 + 9 * 4) goto auth_too_short;
/* We need to extract and decrypt the token and instantiate a session * key for it. This bit, however, is application-specific. If * possible, we use a default parser, but we might end up bumping this * 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); if (ret < 0) goto out;
/* We now have a key instantiated from the decrypted ticket. We can * pass this to the application so that they can parse the ticket * content and we can use the session key it contains to derive the * keys we need. * * Note that we have to switch enctype at this point as the enctype of * the ticket doesn't necessarily match that of the transport.
*/
token = key->payload.data[0];
conn->security_level = token->rxgk->level;
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)) {
ret = PTR_ERR(gk); goto cant_get_token;
}
inconsistent:
ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
rxgk_abort_resp_xdr_align); goto out;
auth_too_short:
ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
rxgk_abort_resp_short_auth); goto out;
short_packet:
ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
rxgk_abort_resp_short_packet); goto out;
cant_get_token: switch (ret) { case -ENOMEM: goto temporary_error; case -EINVAL:
ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
rxgk_abort_resp_internal_error); goto out; case -ENOPKG:
ret = rxrpc_abort_conn(conn, skb, KRB5_PROG_KEYTYPE_NOSUPP,
-EKEYREJECTED, rxgk_abort_resp_nopkg); goto out;
}
temporary_error: /* Ignore the response packet if we got a temporary error such as * ENOMEM. We just want to send the challenge again. Note that we * also come out this way if the ticket decryption fails.
*/ goto out;
}
/* * clear the connection security
*/ staticvoid rxgk_clear(struct rxrpc_connection *conn)
{ int i;
for (i = 0; i < ARRAY_SIZE(conn->rxgk.keys); i++)
rxgk_put(conn->rxgk.keys[i]);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.9Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.