mirror of
https://github.com/torvalds/linux.git
synced 2025-08-15 22:21:42 +02:00
rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
Implement the basic parts of the yfs-rxgk security class (security index 6) to support GSSAPI-negotiated security. Signed-off-by: David Howells <dhowells@redhat.com> cc: Marc Dionne <marc.dionne@auristor.com> cc: Herbert Xu <herbert@gondor.apana.org.au> cc: Chuck Lever <chuck.lever@oracle.com> cc: Simon Horman <horms@kernel.org> cc: linux-afs@lists.infradead.org Link: https://patch.msgid.link/20250411095303.2316168-9-dhowells@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
c86f9b963d
commit
9d1d2b5934
12 changed files with 1694 additions and 5 deletions
|
@ -1182,6 +1182,7 @@ API Function Reference
|
||||||
.. kernel-doc:: net/rxrpc/oob.c
|
.. kernel-doc:: net/rxrpc/oob.c
|
||||||
.. kernel-doc:: net/rxrpc/peer_object.c
|
.. kernel-doc:: net/rxrpc/peer_object.c
|
||||||
.. kernel-doc:: net/rxrpc/recvmsg.c
|
.. kernel-doc:: net/rxrpc/recvmsg.c
|
||||||
|
.. kernel-doc:: net/rxrpc/rxgk.c
|
||||||
.. kernel-doc:: net/rxrpc/rxkad.c
|
.. kernel-doc:: net/rxrpc/rxkad.c
|
||||||
.. kernel-doc:: net/rxrpc/sendmsg.c
|
.. kernel-doc:: net/rxrpc/sendmsg.c
|
||||||
.. kernel-doc:: net/rxrpc/server_key.c
|
.. kernel-doc:: net/rxrpc/server_key.c
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <crypto/krb5.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "afs_fs.h"
|
#include "afs_fs.h"
|
||||||
#include "protocol_yfs.h"
|
#include "protocol_yfs.h"
|
||||||
|
@ -17,6 +18,9 @@
|
||||||
*/
|
*/
|
||||||
static int afs_respond_to_challenge(struct sk_buff *challenge)
|
static int afs_respond_to_challenge(struct sk_buff *challenge)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_RXGK
|
||||||
|
struct krb5_buffer appdata = {};
|
||||||
|
#endif
|
||||||
struct rxrpc_peer *peer;
|
struct rxrpc_peer *peer;
|
||||||
unsigned long peer_data;
|
unsigned long peer_data;
|
||||||
u16 service_id;
|
u16 service_id;
|
||||||
|
@ -44,8 +48,16 @@ static int afs_respond_to_challenge(struct sk_buff *challenge)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (security_index) {
|
switch (security_index) {
|
||||||
|
#ifdef CONFIG_RXKAD
|
||||||
case RXRPC_SECURITY_RXKAD:
|
case RXRPC_SECURITY_RXKAD:
|
||||||
return rxkad_kernel_respond_to_challenge(challenge);
|
return rxkad_kernel_respond_to_challenge(challenge);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_RXGK
|
||||||
|
case RXRPC_SECURITY_RXGK:
|
||||||
|
case RXRPC_SECURITY_YFS_RXGK:
|
||||||
|
return rxgk_kernel_respond_to_challenge(challenge, &appdata);
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return rxrpc_kernel_reject_challenge(challenge, RX_USER_ABORT, -EPROTO,
|
return rxrpc_kernel_reject_challenge(challenge, RX_USER_ABORT, -EPROTO,
|
||||||
|
|
|
@ -69,6 +69,38 @@
|
||||||
EM(rxkad_abort_resp_tkt_sname, "rxkad-resp-tk-sname") \
|
EM(rxkad_abort_resp_tkt_sname, "rxkad-resp-tk-sname") \
|
||||||
EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \
|
EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \
|
||||||
EM(rxkad_abort_resp_version, "rxkad-resp-version") \
|
EM(rxkad_abort_resp_version, "rxkad-resp-version") \
|
||||||
|
/* RxGK security errors */ \
|
||||||
|
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") \
|
||||||
|
EM(rxgk_abort_2_short_encdata, "rxgk2-short-encdata") \
|
||||||
|
EM(rxgk_abort_2_short_header, "rxgk2-short-hdr") \
|
||||||
|
EM(rxgk_abort_bad_key_number, "rxgk-bad-key-num") \
|
||||||
|
EM(rxgk_abort_chall_key_expired, "rxgk-chall-key-exp") \
|
||||||
|
EM(rxgk_abort_chall_no_key, "rxgk-chall-nokey") \
|
||||||
|
EM(rxgk_abort_chall_short, "rxgk-chall-short") \
|
||||||
|
EM(rxgk_abort_resp_auth_dec, "rxgk-resp-auth-dec") \
|
||||||
|
EM(rxgk_abort_resp_bad_callid, "rxgk-resp-bad-callid") \
|
||||||
|
EM(rxgk_abort_resp_bad_nonce, "rxgk-resp-bad-nonce") \
|
||||||
|
EM(rxgk_abort_resp_bad_param, "rxgk-resp-bad-param") \
|
||||||
|
EM(rxgk_abort_resp_call_ctr, "rxgk-resp-call-ctr") \
|
||||||
|
EM(rxgk_abort_resp_call_state, "rxgk-resp-call-state") \
|
||||||
|
EM(rxgk_abort_resp_internal_error, "rxgk-resp-int-error") \
|
||||||
|
EM(rxgk_abort_resp_nopkg, "rxgk-resp-nopkg") \
|
||||||
|
EM(rxgk_abort_resp_short_applen, "rxgk-resp-short-applen") \
|
||||||
|
EM(rxgk_abort_resp_short_auth, "rxgk-resp-short-auth") \
|
||||||
|
EM(rxgk_abort_resp_short_call_list, "rxgk-resp-short-callls") \
|
||||||
|
EM(rxgk_abort_resp_short_packet, "rxgk-resp-short-packet") \
|
||||||
|
EM(rxgk_abort_resp_short_yfs_klen, "rxgk-resp-short-yfs-klen") \
|
||||||
|
EM(rxgk_abort_resp_short_yfs_key, "rxgk-resp-short-yfs-key") \
|
||||||
|
EM(rxgk_abort_resp_short_yfs_tkt, "rxgk-resp-short-yfs-tkt") \
|
||||||
|
EM(rxgk_abort_resp_tok_dec, "rxgk-resp-tok-dec") \
|
||||||
|
EM(rxgk_abort_resp_tok_internal_error, "rxgk-resp-tok-int-err") \
|
||||||
|
EM(rxgk_abort_resp_tok_keyerr, "rxgk-resp-tok-keyerr") \
|
||||||
|
EM(rxgk_abort_resp_tok_nokey, "rxgk-resp-tok-nokey") \
|
||||||
|
EM(rxgk_abort_resp_tok_nopkg, "rxgk-resp-tok-nopkg") \
|
||||||
|
EM(rxgk_abort_resp_tok_short, "rxgk-resp-tok-short") \
|
||||||
|
EM(rxgk_abort_resp_xdr_align, "rxgk-resp-xdr-align") \
|
||||||
/* rxrpc errors */ \
|
/* rxrpc errors */ \
|
||||||
EM(rxrpc_abort_call_improper_term, "call-improper-term") \
|
EM(rxrpc_abort_call_improper_term, "call-improper-term") \
|
||||||
EM(rxrpc_abort_call_reset, "call-reset") \
|
EM(rxrpc_abort_call_reset, "call-reset") \
|
||||||
|
@ -471,6 +503,7 @@
|
||||||
EM(rxrpc_tx_point_call_final_resend, "CallFinalResend") \
|
EM(rxrpc_tx_point_call_final_resend, "CallFinalResend") \
|
||||||
EM(rxrpc_tx_point_conn_abort, "ConnAbort") \
|
EM(rxrpc_tx_point_conn_abort, "ConnAbort") \
|
||||||
EM(rxrpc_tx_point_reject, "Reject") \
|
EM(rxrpc_tx_point_reject, "Reject") \
|
||||||
|
EM(rxrpc_tx_point_rxgk_challenge, "RxGKChall") \
|
||||||
EM(rxrpc_tx_point_rxkad_challenge, "RxkadChall") \
|
EM(rxrpc_tx_point_rxkad_challenge, "RxkadChall") \
|
||||||
EM(rxrpc_tx_point_response, "Response") \
|
EM(rxrpc_tx_point_response, "Response") \
|
||||||
EM(rxrpc_tx_point_version_keepalive, "VerKeepalive") \
|
EM(rxrpc_tx_point_version_keepalive, "VerKeepalive") \
|
||||||
|
@ -489,6 +522,7 @@
|
||||||
|
|
||||||
#define rxrpc_txbuf_traces \
|
#define rxrpc_txbuf_traces \
|
||||||
EM(rxrpc_txbuf_alloc_data, "ALLOC DATA ") \
|
EM(rxrpc_txbuf_alloc_data, "ALLOC DATA ") \
|
||||||
|
EM(rxrpc_txbuf_alloc_response, "ALLOC RESP ") \
|
||||||
EM(rxrpc_txbuf_free, "FREE ") \
|
EM(rxrpc_txbuf_free, "FREE ") \
|
||||||
EM(rxrpc_txbuf_get_buffer, "GET BUFFER ") \
|
EM(rxrpc_txbuf_get_buffer, "GET BUFFER ") \
|
||||||
EM(rxrpc_txbuf_get_trans, "GET TRANS ") \
|
EM(rxrpc_txbuf_get_trans, "GET TRANS ") \
|
||||||
|
@ -496,6 +530,7 @@
|
||||||
EM(rxrpc_txbuf_put_cleaned, "PUT CLEANED") \
|
EM(rxrpc_txbuf_put_cleaned, "PUT CLEANED") \
|
||||||
EM(rxrpc_txbuf_put_nomem, "PUT NOMEM ") \
|
EM(rxrpc_txbuf_put_nomem, "PUT NOMEM ") \
|
||||||
EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \
|
EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \
|
||||||
|
EM(rxrpc_txbuf_put_response_tx, "PUT RESP TX") \
|
||||||
EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \
|
EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \
|
||||||
EM(rxrpc_txbuf_put_trans, "PUT TRANS ") \
|
EM(rxrpc_txbuf_put_trans, "PUT TRANS ") \
|
||||||
EM(rxrpc_txbuf_see_lost, "SEE LOST ") \
|
EM(rxrpc_txbuf_see_lost, "SEE LOST ") \
|
||||||
|
@ -1178,6 +1213,7 @@ TRACE_EVENT(rxrpc_rx_challenge,
|
||||||
__field(u32, version)
|
__field(u32, version)
|
||||||
__field(u32, nonce)
|
__field(u32, nonce)
|
||||||
__field(u32, min_level)
|
__field(u32, min_level)
|
||||||
|
__field(u8, security_ix)
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
|
@ -1186,11 +1222,13 @@ TRACE_EVENT(rxrpc_rx_challenge,
|
||||||
__entry->version = version;
|
__entry->version = version;
|
||||||
__entry->nonce = nonce;
|
__entry->nonce = nonce;
|
||||||
__entry->min_level = min_level;
|
__entry->min_level = min_level;
|
||||||
|
__entry->security_ix = conn->security_ix;
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("C=%08x CHALLENGE %08x v=%x n=%x ml=%x",
|
TP_printk("C=%08x CHALLENGE r=%08x sx=%u v=%x n=%x ml=%x",
|
||||||
__entry->conn,
|
__entry->conn,
|
||||||
__entry->serial,
|
__entry->serial,
|
||||||
|
__entry->security_ix,
|
||||||
__entry->version,
|
__entry->version,
|
||||||
__entry->nonce,
|
__entry->nonce,
|
||||||
__entry->min_level)
|
__entry->min_level)
|
||||||
|
@ -1208,6 +1246,7 @@ TRACE_EVENT(rxrpc_rx_response,
|
||||||
__field(u32, version)
|
__field(u32, version)
|
||||||
__field(u32, kvno)
|
__field(u32, kvno)
|
||||||
__field(u32, ticket_len)
|
__field(u32, ticket_len)
|
||||||
|
__field(u8, security_ix)
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
|
@ -1216,11 +1255,13 @@ TRACE_EVENT(rxrpc_rx_response,
|
||||||
__entry->version = version;
|
__entry->version = version;
|
||||||
__entry->kvno = kvno;
|
__entry->kvno = kvno;
|
||||||
__entry->ticket_len = ticket_len;
|
__entry->ticket_len = ticket_len;
|
||||||
|
__entry->security_ix = conn->security_ix;
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("C=%08x RESPONSE %08x v=%x kvno=%x tl=%x",
|
TP_printk("C=%08x RESPONSE r=%08x sx=%u v=%x kvno=%x tl=%x",
|
||||||
__entry->conn,
|
__entry->conn,
|
||||||
__entry->serial,
|
__entry->serial,
|
||||||
|
__entry->security_ix,
|
||||||
__entry->version,
|
__entry->version,
|
||||||
__entry->kvno,
|
__entry->kvno,
|
||||||
__entry->ticket_len)
|
__entry->ticket_len)
|
||||||
|
|
|
@ -41,6 +41,8 @@ rxrpc-$(CONFIG_PROC_FS) += proc.o
|
||||||
rxrpc-$(CONFIG_RXKAD) += rxkad.o
|
rxrpc-$(CONFIG_RXKAD) += rxkad.o
|
||||||
rxrpc-$(CONFIG_SYSCTL) += sysctl.o
|
rxrpc-$(CONFIG_SYSCTL) += sysctl.o
|
||||||
rxrpc-$(CONFIG_RXGK) += \
|
rxrpc-$(CONFIG_RXGK) += \
|
||||||
|
rxgk.o \
|
||||||
|
rxgk_app.o \
|
||||||
rxgk_kdf.o
|
rxgk_kdf.o
|
||||||
|
|
||||||
obj-$(CONFIG_RXPERF) += rxperf.o
|
obj-$(CONFIG_RXPERF) += rxperf.o
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct key_preparsed_payload;
|
||||||
struct rxrpc_connection;
|
struct rxrpc_connection;
|
||||||
struct rxrpc_txbuf;
|
struct rxrpc_txbuf;
|
||||||
struct rxrpc_txqueue;
|
struct rxrpc_txqueue;
|
||||||
|
struct rxgk_context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark applied to socket buffers in skb->mark. skb->priority is used
|
* Mark applied to socket buffers in skb->mark. skb->priority is used
|
||||||
|
@ -312,6 +313,11 @@ struct rxrpc_security {
|
||||||
|
|
||||||
/* clear connection security */
|
/* clear connection security */
|
||||||
void (*clear)(struct rxrpc_connection *);
|
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,
|
||||||
|
struct key **_key);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -559,7 +565,10 @@ struct rxrpc_connection {
|
||||||
u32 nonce; /* response re-use preventer */
|
u32 nonce; /* response re-use preventer */
|
||||||
} rxkad;
|
} rxkad;
|
||||||
struct {
|
struct {
|
||||||
|
struct rxgk_context *keys[1];
|
||||||
u64 start_time; /* The start time for TK derivation */
|
u64 start_time; /* The start time for TK derivation */
|
||||||
|
u8 nonce[20]; /* Response re-use preventer */
|
||||||
|
u32 enctype; /* Kerberos 5 encoding type */
|
||||||
} rxgk;
|
} rxgk;
|
||||||
};
|
};
|
||||||
struct sk_buff *tx_response; /* Response packet to be transmitted */
|
struct sk_buff *tx_response; /* Response packet to be transmitted */
|
||||||
|
@ -903,6 +912,8 @@ struct rxrpc_txbuf {
|
||||||
unsigned short len; /* Amount of data in buffer */
|
unsigned short len; /* Amount of data in buffer */
|
||||||
unsigned short space; /* Remaining data space */
|
unsigned short space; /* Remaining data space */
|
||||||
unsigned short offset; /* Offset of fill point */
|
unsigned short offset; /* Offset of fill point */
|
||||||
|
unsigned short crypto_header; /* Size of crypto header */
|
||||||
|
unsigned short sec_header; /* Size of security header */
|
||||||
unsigned short pkt_len; /* Size of packet content */
|
unsigned short pkt_len; /* Size of packet content */
|
||||||
unsigned short alloc_size; /* Amount of bufferage allocated */
|
unsigned short alloc_size; /* Amount of bufferage allocated */
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
@ -1339,6 +1350,7 @@ int rxrpc_sendmsg_oob(struct rxrpc_sock *rx, struct msghdr *msg, size_t len);
|
||||||
/*
|
/*
|
||||||
* output.c
|
* output.c
|
||||||
*/
|
*/
|
||||||
|
ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len);
|
||||||
void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason,
|
void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason,
|
||||||
rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why);
|
rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why);
|
||||||
void rxrpc_send_probe_for_pmtud(struct rxrpc_call *call);
|
void rxrpc_send_probe_for_pmtud(struct rxrpc_call *call);
|
||||||
|
@ -1411,6 +1423,11 @@ void rxrpc_call_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why,
|
||||||
ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans);
|
ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans);
|
||||||
void rxrpc_call_init_rtt(struct rxrpc_call *call);
|
void rxrpc_call_init_rtt(struct rxrpc_call *call);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rxgk.c
|
||||||
|
*/
|
||||||
|
extern const struct rxrpc_security rxgk_yfs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rxkad.c
|
* rxkad.c
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
|
extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
|
||||||
|
|
||||||
static ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len)
|
ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len)
|
||||||
{
|
{
|
||||||
struct sockaddr *sa = msg->msg_name;
|
struct sockaddr *sa = msg->msg_name;
|
||||||
struct sock *sk = socket->sk;
|
struct sock *sk = socket->sk;
|
||||||
|
|
|
@ -181,4 +181,24 @@ struct rxkad_response {
|
||||||
__be32 ticket_len; /* Kerberos ticket length */
|
__be32 ticket_len; /* Kerberos ticket length */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GSSAPI security type-4 and type-6 data header.
|
||||||
|
*/
|
||||||
|
struct rxgk_header {
|
||||||
|
__be32 epoch;
|
||||||
|
__be32 cid;
|
||||||
|
__be32 call_number;
|
||||||
|
__be32 seq;
|
||||||
|
__be32 sec_index;
|
||||||
|
__be32 data_len;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GSSAPI security type-4 and type-6 response packet header.
|
||||||
|
*/
|
||||||
|
struct rxgk_response {
|
||||||
|
__be64 start_time;
|
||||||
|
__be32 token_len;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#endif /* _LINUX_RXRPC_PACKET_H */
|
#endif /* _LINUX_RXRPC_PACKET_H */
|
||||||
|
|
1215
net/rxrpc/rxgk.c
Normal file
1215
net/rxrpc/rxgk.c
Normal file
File diff suppressed because it is too large
Load diff
285
net/rxrpc/rxgk_app.c
Normal file
285
net/rxrpc/rxgk_app.c
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/* Application-specific bits for GSSAPI-based RxRPC security
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
|
||||||
|
* Written by David Howells (dhowells@redhat.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/net.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/key-type.h>
|
||||||
|
#include "ar-internal.h"
|
||||||
|
#include "rxgk_common.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode a default-style YFS ticket in a response and turn it into an
|
||||||
|
* rxrpc-type key.
|
||||||
|
*
|
||||||
|
* struct rxgk_key {
|
||||||
|
* afs_uint32 enctype;
|
||||||
|
* opaque key<>;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* struct RXGK_AuthName {
|
||||||
|
* afs_int32 kind;
|
||||||
|
* opaque data<AUTHDATAMAX>;
|
||||||
|
* opaque display<AUTHPRINTABLEMAX>;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* struct RXGK_Token {
|
||||||
|
* rxgk_key K0;
|
||||||
|
* RXGK_Level level;
|
||||||
|
* rxgkTime starttime;
|
||||||
|
* afs_int32 lifetime;
|
||||||
|
* afs_int32 bytelife;
|
||||||
|
* rxgkTime expirationtime;
|
||||||
|
* struct RXGK_AuthName identities<>;
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
|
unsigned int ticket_offset, unsigned int ticket_len,
|
||||||
|
struct key **_key)
|
||||||
|
{
|
||||||
|
struct rxrpc_key_token *token;
|
||||||
|
const struct cred *cred = current_cred(); // TODO - use socket creds
|
||||||
|
struct key *key;
|
||||||
|
size_t pre_ticket_len, payload_len;
|
||||||
|
unsigned int klen, enctype;
|
||||||
|
void *payload, *ticket;
|
||||||
|
__be32 *t, *p, *q, tmp[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
_enter("");
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
enctype = ntohl(tmp[0]);
|
||||||
|
klen = ntohl(tmp[1]);
|
||||||
|
|
||||||
|
if (klen > ticket_len - 10 * sizeof(__be32))
|
||||||
|
return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
|
||||||
|
rxgk_abort_resp_short_yfs_key);
|
||||||
|
|
||||||
|
pre_ticket_len = ((5 + 14) * sizeof(__be32) +
|
||||||
|
xdr_round_up(klen) +
|
||||||
|
sizeof(__be32));
|
||||||
|
payload_len = pre_ticket_len + xdr_round_up(ticket_len);
|
||||||
|
|
||||||
|
payload = kzalloc(payload_len, GFP_NOFS);
|
||||||
|
if (!payload)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* We need to fill out the XDR form for a key payload that we can pass
|
||||||
|
* to add_key(). Start by copying in the ticket so that we can parse
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill out the form header. */
|
||||||
|
p = payload;
|
||||||
|
p[0] = htonl(0); /* Flags */
|
||||||
|
p[1] = htonl(1); /* len(cellname) */
|
||||||
|
p[2] = htonl(0x20000000); /* Cellname " " */
|
||||||
|
p[3] = htonl(1); /* #tokens */
|
||||||
|
p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) +
|
||||||
|
xdr_round_up(ticket_len)); /* Token len */
|
||||||
|
|
||||||
|
/* Now fill in the body. Most of this we can just scrape directly from
|
||||||
|
* the ticket.
|
||||||
|
*/
|
||||||
|
t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen);
|
||||||
|
q = payload + 5 * sizeof(__be32);
|
||||||
|
q[0] = htonl(RXRPC_SECURITY_YFS_RXGK);
|
||||||
|
q[1] = t[1]; /* begintime - msw */
|
||||||
|
q[2] = t[2]; /* - lsw */
|
||||||
|
q[3] = t[5]; /* endtime - msw */
|
||||||
|
q[4] = t[6]; /* - lsw */
|
||||||
|
q[5] = 0; /* level - msw */
|
||||||
|
q[6] = t[0]; /* - lsw */
|
||||||
|
q[7] = 0; /* lifetime - msw */
|
||||||
|
q[8] = t[3]; /* - lsw */
|
||||||
|
q[9] = 0; /* bytelife - msw */
|
||||||
|
q[10] = t[4]; /* - lsw */
|
||||||
|
q[11] = 0; /* enctype - msw */
|
||||||
|
q[12] = htonl(enctype); /* - lsw */
|
||||||
|
q[13] = htonl(klen); /* Key length */
|
||||||
|
|
||||||
|
q += 14;
|
||||||
|
|
||||||
|
memcpy(q, ticket + sizeof(__be32) * 2, klen);
|
||||||
|
q += xdr_round_up(klen) / 4;
|
||||||
|
q[0] = htonl(ticket_len);
|
||||||
|
q++;
|
||||||
|
if (WARN_ON((unsigned long)q != (unsigned long)ticket)) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ticket read in with skb_copy_bits above */
|
||||||
|
q += xdr_round_up(ticket_len) / 4;
|
||||||
|
if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now turn that into a key. */
|
||||||
|
key = key_alloc(&key_type_rxrpc, "x",
|
||||||
|
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, // TODO: Use socket owner
|
||||||
|
KEY_USR_VIEW,
|
||||||
|
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||||
|
if (IS_ERR(key)) {
|
||||||
|
_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_debug("key %d", key_serial(key));
|
||||||
|
|
||||||
|
ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error_key;
|
||||||
|
|
||||||
|
token = key->payload.data[0];
|
||||||
|
token->no_leak_key = true;
|
||||||
|
*_key = key;
|
||||||
|
key = NULL;
|
||||||
|
ret = 0;
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
error_key:
|
||||||
|
key_put(key);
|
||||||
|
error:
|
||||||
|
kfree_sensitive(payload);
|
||||||
|
_leave(" = %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the token and set up a session key from the details.
|
||||||
|
*
|
||||||
|
* struct RXGK_TokenContainer {
|
||||||
|
* afs_int32 kvno;
|
||||||
|
* afs_int32 enctype;
|
||||||
|
* opaque encrypted_token<>;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* [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,
|
||||||
|
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;
|
||||||
|
u32 kvno, enctype;
|
||||||
|
int ret, ec;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__be32 kvno;
|
||||||
|
__be32 enctype;
|
||||||
|
__be32 token_len;
|
||||||
|
} container;
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
|
||||||
|
rxgk_abort_resp_tok_short);
|
||||||
|
|
||||||
|
kvno = ntohl(container.kvno);
|
||||||
|
enctype = ntohl(container.enctype);
|
||||||
|
ticket_len = ntohl(container.token_len);
|
||||||
|
ticket_offset = token_offset + sizeof(container);
|
||||||
|
|
||||||
|
if (xdr_round_up(ticket_len) > token_len - 3 * 4)
|
||||||
|
return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
|
||||||
|
rxgk_abort_resp_tok_short);
|
||||||
|
|
||||||
|
_debug("KVNO %u", kvno);
|
||||||
|
_debug("ENC %u", enctype);
|
||||||
|
_debug("TLEN %u", ticket_len);
|
||||||
|
|
||||||
|
server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype);
|
||||||
|
if (IS_ERR(server_key))
|
||||||
|
goto cant_get_server_key;
|
||||||
|
|
||||||
|
down_read(&server_key->sem);
|
||||||
|
server_secret = (const void *)&server_key->payload.data[2];
|
||||||
|
ret = rxgk_set_up_token_cipher(server_secret, &token_enc, enctype, &krb5, GFP_NOFS);
|
||||||
|
up_read(&server_key->sem);
|
||||||
|
key_put(server_key);
|
||||||
|
if (ret < 0)
|
||||||
|
goto cant_get_token;
|
||||||
|
|
||||||
|
/* We can now decrypt and parse the token/ticket. This allows us to
|
||||||
|
* 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);
|
||||||
|
crypto_free_aead(token_enc);
|
||||||
|
token_enc = NULL;
|
||||||
|
if (ret < 0)
|
||||||
|
return rxrpc_abort_conn(conn, skb, ec, ret,
|
||||||
|
rxgk_abort_resp_tok_dec);
|
||||||
|
|
||||||
|
ret = conn->security->default_decode_ticket(conn, skb, ticket_offset,
|
||||||
|
ticket_len, _key);
|
||||||
|
if (ret < 0)
|
||||||
|
goto cant_get_token;
|
||||||
|
|
||||||
|
_leave(" = 0");
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
cant_get_server_key:
|
||||||
|
ret = PTR_ERR(server_key);
|
||||||
|
switch (ret) {
|
||||||
|
case -ENOMEM:
|
||||||
|
goto temporary_error;
|
||||||
|
case -ENOKEY:
|
||||||
|
case -EKEYREJECTED:
|
||||||
|
case -EKEYEXPIRED:
|
||||||
|
case -EKEYREVOKED:
|
||||||
|
case -EPERM:
|
||||||
|
return rxrpc_abort_conn(conn, skb, RXGK_BADKEYNO, -EKEYREJECTED,
|
||||||
|
rxgk_abort_resp_tok_nokey);
|
||||||
|
default:
|
||||||
|
return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
|
||||||
|
rxgk_abort_resp_tok_keyerr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cant_get_token:
|
||||||
|
switch (ret) {
|
||||||
|
case -ENOMEM:
|
||||||
|
goto temporary_error;
|
||||||
|
case -EINVAL:
|
||||||
|
return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
|
||||||
|
rxgk_abort_resp_tok_internal_error);
|
||||||
|
case -ENOPKG:
|
||||||
|
return rxrpc_abort_conn(conn, skb, KRB5_PROG_KEYTYPE_NOSUPP,
|
||||||
|
-EKEYREJECTED, rxgk_abort_resp_tok_nopkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -33,6 +33,19 @@ struct rxgk_context {
|
||||||
struct crypto_aead *resp_enc; /* Response packet enc key */
|
struct crypto_aead *resp_enc; /* Response packet enc key */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define xdr_round_up(x) (round_up((x), sizeof(__be32)))
|
||||||
|
#define xdr_object_len(x) (4 + xdr_round_up(x))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rxgk_app.c
|
||||||
|
*/
|
||||||
|
int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
|
||||||
|
unsigned int ticket_offset, 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,
|
||||||
|
struct key **_key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rxgk_kdf.c
|
* rxgk_kdf.c
|
||||||
*/
|
*/
|
||||||
|
@ -46,3 +59,81 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
|
||||||
unsigned int enctype,
|
unsigned int enctype,
|
||||||
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 -EPROTO:
|
||||||
|
case -EBADMSG:
|
||||||
|
*_error_code = RXGK_SEALEDINCON;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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 -EPROTO:
|
||||||
|
case -EBADMSG:
|
||||||
|
*_error_code = RXGK_SEALEDINCON;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -177,6 +177,8 @@ static struct rxrpc_txbuf *rxkad_alloc_txbuf(struct rxrpc_call *call, size_t rem
|
||||||
if (!txb)
|
if (!txb)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
txb->crypto_header = 0;
|
||||||
|
txb->sec_header = shdr;
|
||||||
txb->offset += shdr;
|
txb->offset += shdr;
|
||||||
txb->space = part;
|
txb->space = part;
|
||||||
return txb;
|
return txb;
|
||||||
|
|
|
@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = {
|
||||||
#ifdef CONFIG_RXKAD
|
#ifdef CONFIG_RXKAD
|
||||||
[RXRPC_SECURITY_RXKAD] = &rxkad,
|
[RXRPC_SECURITY_RXKAD] = &rxkad,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_RXGK
|
||||||
|
[RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
int __init rxrpc_init_security(void)
|
int __init rxrpc_init_security(void)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue