Commit 0ca100ff authored by David Howells's avatar David Howells Committed by Jakub Kicinski
Browse files

rxrpc: Add YFS RxGK (GSSAPI) security class



Add support for the YFS-variant RxGK security class to support
GSSAPI-derived authentication.  This also allows the use of better crypto
over the rxkad security class.

The key payload is XDR encoded of the form:

    typedef int64_t opr_time;

    const AFSTOKEN_RK_TIX_MAX = 12000; 	/* Matches entry in rxkad.h */

    struct token_rxkad {
	afs_int32 viceid;
	afs_int32 kvno;
	afs_int64 key;
	afs_int32 begintime;
	afs_int32 endtime;
	afs_int32 primary_flag;
	opaque ticket<AFSTOKEN_RK_TIX_MAX>;
    };

    struct token_rxgk {
	opr_time begintime;
	opr_time endtime;
	afs_int64 level;
	afs_int64 lifetime;
	afs_int64 bytelife;
	afs_int64 enctype;
	opaque key<>;
	opaque ticket<>;
    };

    const AFSTOKEN_UNION_NOAUTH = 0;
    const AFSTOKEN_UNION_KAD = 2;
    const AFSTOKEN_UNION_YFSGK = 6;

    union ktc_tokenUnion switch (afs_int32 type) {
	case AFSTOKEN_UNION_KAD:
	    token_rxkad kad;
	case AFSTOKEN_UNION_YFSGK:
	    token_rxgk  gk;
    };

    const AFSTOKEN_LENGTH_MAX = 16384;
    typedef opaque token_opaque<AFSTOKEN_LENGTH_MAX>;

    const AFSTOKEN_MAX = 8;
    const AFSTOKEN_CELL_MAX = 64;

    struct ktc_setTokenData {
	afs_int32 flags;
	string cell<AFSTOKEN_CELL_MAX>;
	token_opaque tokens<AFSTOKEN_MAX>;
    };

The parser for the basic token struct is already present, as is the rxkad
token type.  This adds a parser for the rxgk token type.

Signed-off-by: default avatarDavid 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-7-dhowells@redhat.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 01af6426
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#define _KEYS_RXRPC_TYPE_H

#include <linux/key.h>
#include <crypto/krb5.h>

/*
 * key type for AF_RXRPC keys
@@ -31,6 +32,21 @@ struct rxkad_key {
	u8	ticket[];		/* the encrypted ticket */
};

/*
 * RxRPC key for YFS-RxGK (type-6 security)
 */
struct rxgk_key {
	s64		begintime;	/* Time at which the ticket starts */
	s64		endtime;	/* Time at which the ticket ends */
	u64		lifetime;	/* Maximum lifespan of a connection (seconds) */
	u64		bytelife;	/* Maximum number of bytes on a connection */
	unsigned int	enctype;	/* Encoding type */
	s8		level;		/* Negotiated security RXRPC_SECURITY_PLAIN/AUTH/ENCRYPT */
	struct krb5_buffer key;		/* Master key, K0 */
	struct krb5_buffer ticket;	/* Ticket to be passed to server */
	u8		_key[];		/* Key storage */
};

/*
 * list of tokens attached to an rxrpc key
 */
@@ -40,6 +56,7 @@ struct rxrpc_key_token {
	struct rxrpc_key_token *next;	/* the next token in the list */
	union {
		struct rxkad_key *kad;
		struct rxgk_key *rxgk;
	};
};

+185 −0
Original line number Diff line number Diff line
@@ -129,6 +129,160 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
	return 0;
}

static u64 xdr_dec64(const __be32 *xdr)
{
	return (u64)ntohl(xdr[0]) << 32 | (u64)ntohl(xdr[1]);
}

static time64_t rxrpc_s64_to_time64(s64 time_in_100ns)
{
	bool neg = false;
	u64 tmp = time_in_100ns;

	if (time_in_100ns < 0) {
		tmp = -time_in_100ns;
		neg = true;
	}
	do_div(tmp, 10000000);
	return neg ? -tmp : tmp;
}

/*
 * Parse a YFS-RxGK type XDR format token
 * - the caller guarantees we have at least 4 words
 *
 * struct token_rxgk {
 *	opr_time begintime;
 *	opr_time endtime;
 *	afs_int64 level;
 *	afs_int64 lifetime;
 *	afs_int64 bytelife;
 *	afs_int64 enctype;
 *	opaque key<>;
 *	opaque ticket<>;
 * };
 */
static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep,
				       size_t datalen,
				       const __be32 *xdr, unsigned int toklen)
{
	struct rxrpc_key_token *token, **pptoken;
	time64_t expiry;
	size_t plen;
	const __be32 *ticket, *key;
	s64 tmp;
	u32 tktlen, keylen;

	_enter(",{%x,%x,%x,%x},%x",
	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
	       toklen);

	if (6 * 2 + 2 > toklen / 4)
		goto reject;

	key = xdr + (6 * 2 + 1);
	keylen = ntohl(key[-1]);
	_debug("keylen: %x", keylen);
	keylen = round_up(keylen, 4);
	if ((6 * 2 + 2) * 4 + keylen > toklen)
		goto reject;

	ticket = xdr + (6 * 2 + 1 + (keylen / 4) + 1);
	tktlen = ntohl(ticket[-1]);
	_debug("tktlen: %x", tktlen);
	tktlen = round_up(tktlen, 4);
	if ((6 * 2 + 2) * 4 + keylen + tktlen != toklen) {
		kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]",
		       (6 * 2 + 2) * 4 + keylen + tktlen, toklen,
		       keylen, tktlen);
		goto reject;
	}

	plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen;
	prep->quotalen = datalen + plen;

	plen -= sizeof(*token);
	token = kzalloc(sizeof(*token), GFP_KERNEL);
	if (!token)
		goto nomem;

	token->rxgk = kzalloc(sizeof(*token->rxgk) + keylen, GFP_KERNEL);
	if (!token->rxgk)
		goto nomem_token;

	token->security_index	= RXRPC_SECURITY_YFS_RXGK;
	token->rxgk->begintime	= xdr_dec64(xdr + 0 * 2);
	token->rxgk->endtime	= xdr_dec64(xdr + 1 * 2);
	token->rxgk->level	= tmp = xdr_dec64(xdr + 2 * 2);
	if (tmp < -1LL || tmp > RXRPC_SECURITY_ENCRYPT)
		goto reject_token;
	token->rxgk->lifetime	= xdr_dec64(xdr + 3 * 2);
	token->rxgk->bytelife	= xdr_dec64(xdr + 4 * 2);
	token->rxgk->enctype	= tmp = xdr_dec64(xdr + 5 * 2);
	if (tmp < 0 || tmp > UINT_MAX)
		goto reject_token;
	token->rxgk->key.len	= ntohl(key[-1]);
	token->rxgk->key.data	= token->rxgk->_key;
	token->rxgk->ticket.len = ntohl(ticket[-1]);

	if (token->rxgk->endtime != 0) {
		expiry = rxrpc_s64_to_time64(token->rxgk->endtime);
		if (expiry < 0)
			goto expired;
		if (expiry < prep->expiry)
			prep->expiry = expiry;
	}

	memcpy(token->rxgk->key.data, key, token->rxgk->key.len);

	/* Pad the ticket so that we can use it directly in XDR */
	token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4),
					   GFP_KERNEL);
	if (!token->rxgk->ticket.data)
		goto nomem_yrxgk;
	memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len);

	_debug("SCIX: %u",	token->security_index);
	_debug("EXPY: %llx",	token->rxgk->endtime);
	_debug("LIFE: %llx",	token->rxgk->lifetime);
	_debug("BYTE: %llx",	token->rxgk->bytelife);
	_debug("ENC : %u",	token->rxgk->enctype);
	_debug("LEVL: %u",	token->rxgk->level);
	_debug("KLEN: %u",	token->rxgk->key.len);
	_debug("TLEN: %u",	token->rxgk->ticket.len);
	_debug("KEY0: %*phN",	token->rxgk->key.len, token->rxgk->key.data);
	_debug("TICK: %*phN",
	       min_t(u32, token->rxgk->ticket.len, 32), token->rxgk->ticket.data);

	/* count the number of tokens attached */
	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);

	/* attach the data */
	for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
	     *pptoken;
	     pptoken = &(*pptoken)->next)
		continue;
	*pptoken = token;

	_leave(" = 0");
	return 0;

nomem_yrxgk:
	kfree(token->rxgk);
nomem_token:
	kfree(token);
nomem:
	return -ENOMEM;
reject_token:
	kfree(token);
reject:
	return -EKEYREJECTED;
expired:
	kfree(token->rxgk);
	kfree(token);
	return -EKEYEXPIRED;
}

/*
 * attempt to parse the data as the XDR format
 * - the caller guarantees we have more than 7 words
@@ -228,6 +382,9 @@ static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
		case RXRPC_SECURITY_RXKAD:
			ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen);
			break;
		case RXRPC_SECURITY_YFS_RXGK:
			ret2 = rxrpc_preparse_xdr_yfs_rxgk(prep, datalen, token, toklen);
			break;
		default:
			ret2 = -EPROTONOSUPPORT;
			break;
@@ -390,6 +547,10 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token)
		case RXRPC_SECURITY_RXKAD:
			kfree(token->kad);
			break;
		case RXRPC_SECURITY_YFS_RXGK:
			kfree(token->rxgk->ticket.data);
			kfree(token->rxgk);
			break;
		default:
			pr_err("Unknown token type %x on rxrpc key\n",
			       token->security_index);
@@ -433,6 +594,9 @@ static void rxrpc_describe(const struct key *key, struct seq_file *m)
		case RXRPC_SECURITY_RXKAD:
			seq_puts(m, "ka");
			break;
		case RXRPC_SECURITY_YFS_RXGK:
			seq_puts(m, "ygk");
			break;
		default: /* we have a ticket we can't encode */
			seq_printf(m, "%u", token->security_index);
			break;
@@ -597,6 +761,13 @@ static long rxrpc_read(const struct key *key,
				toksize += RND(token->kad->ticket_len);
			break;

		case RXRPC_SECURITY_YFS_RXGK:
			toksize += 6 * 8 + 2 * 4;
			if (!token->no_leak_key)
				toksize += RND(token->rxgk->key.len);
			toksize += RND(token->rxgk->ticket.len);
			break;

		default: /* we have a ticket we can't encode */
			pr_err("Unsupported key token type (%u)\n",
			       token->security_index);
@@ -676,6 +847,20 @@ static long rxrpc_read(const struct key *key,
				ENCODE_DATA(token->kad->ticket_len, token->kad->ticket);
			break;

		case RXRPC_SECURITY_YFS_RXGK:
			ENCODE64(token->rxgk->begintime);
			ENCODE64(token->rxgk->endtime);
			ENCODE64(token->rxgk->level);
			ENCODE64(token->rxgk->lifetime);
			ENCODE64(token->rxgk->bytelife);
			ENCODE64(token->rxgk->enctype);
			if (token->no_leak_key)
				ENCODE(0);
			else
				ENCODE_DATA(token->rxgk->key.len, token->rxgk->key.data);
			ENCODE_DATA(token->rxgk->ticket.len, token->rxgk->ticket.data);
			break;

		default:
			pr_err("Unsupported key token type (%u)\n",
			       token->security_index);