162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * COPYRIGHT (c) 2008
362306a36Sopenharmony_ci * The Regents of the University of Michigan
462306a36Sopenharmony_ci * ALL RIGHTS RESERVED
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is granted to use, copy, create derivative works
762306a36Sopenharmony_ci * and redistribute this software and such derivative works
862306a36Sopenharmony_ci * for any purpose, so long as the name of The University of
962306a36Sopenharmony_ci * Michigan is not used in any advertising or publicity
1062306a36Sopenharmony_ci * pertaining to the use of distribution of this software
1162306a36Sopenharmony_ci * without specific, written prior authorization.  If the
1262306a36Sopenharmony_ci * above copyright notice or any other identification of the
1362306a36Sopenharmony_ci * University of Michigan is included in any copy of any
1462306a36Sopenharmony_ci * portion of this software, then the disclaimer below must
1562306a36Sopenharmony_ci * also be included.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
1862306a36Sopenharmony_ci * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
1962306a36Sopenharmony_ci * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
2062306a36Sopenharmony_ci * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
2162306a36Sopenharmony_ci * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
2262306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
2362306a36Sopenharmony_ci * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
2462306a36Sopenharmony_ci * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
2562306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
2662306a36Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
2762306a36Sopenharmony_ci * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
2862306a36Sopenharmony_ci * SUCH DAMAGES.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <crypto/skcipher.h>
3262306a36Sopenharmony_ci#include <linux/types.h>
3362306a36Sopenharmony_ci#include <linux/jiffies.h>
3462306a36Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h>
3562306a36Sopenharmony_ci#include <linux/pagemap.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "gss_krb5_internal.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
4062306a36Sopenharmony_ci# define RPCDBG_FACILITY	RPCDBG_AUTH
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * We can shift data by up to LOCAL_BUF_LEN bytes in a pass.  If we need
4562306a36Sopenharmony_ci * to do more than that, we shift repeatedly.  Kevin Coffman reports
4662306a36Sopenharmony_ci * seeing 28 bytes as the value used by Microsoft clients and servers
4762306a36Sopenharmony_ci * with AES, so this constant is chosen to allow handling 28 in one pass
4862306a36Sopenharmony_ci * without using too much stack space.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * If that proves to a problem perhaps we could use a more clever
5162306a36Sopenharmony_ci * algorithm.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#define LOCAL_BUF_LEN 32u
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	char head[LOCAL_BUF_LEN];
5862306a36Sopenharmony_ci	char tmp[LOCAL_BUF_LEN];
5962306a36Sopenharmony_ci	unsigned int this_len, i;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	BUG_ON(shift > LOCAL_BUF_LEN);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	read_bytes_from_xdr_buf(buf, 0, head, shift);
6462306a36Sopenharmony_ci	for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
6562306a36Sopenharmony_ci		this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
6662306a36Sopenharmony_ci		read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
6762306a36Sopenharmony_ci		write_bytes_to_xdr_buf(buf, i, tmp, this_len);
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void _rotate_left(struct xdr_buf *buf, unsigned int shift)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int shifted = 0;
7562306a36Sopenharmony_ci	int this_shift;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	shift %= buf->len;
7862306a36Sopenharmony_ci	while (shifted < shift) {
7962306a36Sopenharmony_ci		this_shift = min(shift - shifted, LOCAL_BUF_LEN);
8062306a36Sopenharmony_ci		rotate_buf_a_little(buf, this_shift);
8162306a36Sopenharmony_ci		shifted += this_shift;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct xdr_buf subbuf;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
9062306a36Sopenharmony_ci	_rotate_left(&subbuf, shift);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciu32
9462306a36Sopenharmony_cigss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
9562306a36Sopenharmony_ci		 struct xdr_buf *buf, struct page **pages)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	u8		*ptr;
9862306a36Sopenharmony_ci	time64_t	now;
9962306a36Sopenharmony_ci	u8		flags = 0x00;
10062306a36Sopenharmony_ci	__be16		*be16ptr;
10162306a36Sopenharmony_ci	__be64		*be64ptr;
10262306a36Sopenharmony_ci	u32		err;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	dprintk("RPC:       %s\n", __func__);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* make room for gss token header */
10762306a36Sopenharmony_ci	if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN))
10862306a36Sopenharmony_ci		return GSS_S_FAILURE;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* construct gss token header */
11162306a36Sopenharmony_ci	ptr = buf->head[0].iov_base + offset;
11262306a36Sopenharmony_ci	*ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
11362306a36Sopenharmony_ci	*ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
11662306a36Sopenharmony_ci		flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
11762306a36Sopenharmony_ci	if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
11862306a36Sopenharmony_ci		flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
11962306a36Sopenharmony_ci	/* We always do confidentiality in wrap tokens */
12062306a36Sopenharmony_ci	flags |= KG2_TOKEN_FLAG_SEALED;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	*ptr++ = flags;
12362306a36Sopenharmony_ci	*ptr++ = 0xff;
12462306a36Sopenharmony_ci	be16ptr = (__be16 *)ptr;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	*be16ptr++ = 0;
12762306a36Sopenharmony_ci	/* "inner" token header always uses 0 for RRC */
12862306a36Sopenharmony_ci	*be16ptr++ = 0;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	be64ptr = (__be64 *)be16ptr;
13162306a36Sopenharmony_ci	*be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages);
13462306a36Sopenharmony_ci	if (err)
13562306a36Sopenharmony_ci		return err;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	now = ktime_get_real_seconds();
13862306a36Sopenharmony_ci	return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciu32
14262306a36Sopenharmony_cigss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
14362306a36Sopenharmony_ci		   struct xdr_buf *buf, unsigned int *slack,
14462306a36Sopenharmony_ci		   unsigned int *align)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	time64_t	now;
14762306a36Sopenharmony_ci	u8		*ptr;
14862306a36Sopenharmony_ci	u8		flags = 0x00;
14962306a36Sopenharmony_ci	u16		ec, rrc;
15062306a36Sopenharmony_ci	int		err;
15162306a36Sopenharmony_ci	u32		headskip, tailskip;
15262306a36Sopenharmony_ci	u8		decrypted_hdr[GSS_KRB5_TOK_HDR_LEN];
15362306a36Sopenharmony_ci	unsigned int	movelen;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	dprintk("RPC:       %s\n", __func__);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	ptr = buf->head[0].iov_base + offset;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
16162306a36Sopenharmony_ci		return GSS_S_DEFECTIVE_TOKEN;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	flags = ptr[2];
16462306a36Sopenharmony_ci	if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
16562306a36Sopenharmony_ci	    (kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
16662306a36Sopenharmony_ci		return GSS_S_BAD_SIG;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) {
16962306a36Sopenharmony_ci		dprintk("%s: token missing expected sealed flag\n", __func__);
17062306a36Sopenharmony_ci		return GSS_S_DEFECTIVE_TOKEN;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (ptr[3] != 0xff)
17462306a36Sopenharmony_ci		return GSS_S_DEFECTIVE_TOKEN;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ec = be16_to_cpup((__be16 *)(ptr + 4));
17762306a36Sopenharmony_ci	rrc = be16_to_cpup((__be16 *)(ptr + 6));
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/*
18062306a36Sopenharmony_ci	 * NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
18162306a36Sopenharmony_ci	 * doesn't want it checked; see page 6 of rfc 2203.
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (rrc != 0)
18562306a36Sopenharmony_ci		rotate_left(offset + 16, buf, rrc);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf,
18862306a36Sopenharmony_ci				     &headskip, &tailskip);
18962306a36Sopenharmony_ci	if (err)
19062306a36Sopenharmony_ci		return GSS_S_FAILURE;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * Retrieve the decrypted gss token header and verify
19462306a36Sopenharmony_ci	 * it against the original
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	err = read_bytes_from_xdr_buf(buf,
19762306a36Sopenharmony_ci				len - GSS_KRB5_TOK_HDR_LEN - tailskip,
19862306a36Sopenharmony_ci				decrypted_hdr, GSS_KRB5_TOK_HDR_LEN);
19962306a36Sopenharmony_ci	if (err) {
20062306a36Sopenharmony_ci		dprintk("%s: error %u getting decrypted_hdr\n", __func__, err);
20162306a36Sopenharmony_ci		return GSS_S_FAILURE;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	if (memcmp(ptr, decrypted_hdr, 6)
20462306a36Sopenharmony_ci				|| memcmp(ptr + 8, decrypted_hdr + 8, 8)) {
20562306a36Sopenharmony_ci		dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__);
20662306a36Sopenharmony_ci		return GSS_S_FAILURE;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* do sequencing checks */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* it got through unscathed.  Make sure the context is unexpired */
21262306a36Sopenharmony_ci	now = ktime_get_real_seconds();
21362306a36Sopenharmony_ci	if (now > kctx->endtime)
21462306a36Sopenharmony_ci		return GSS_S_CONTEXT_EXPIRED;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * Move the head data back to the right position in xdr_buf.
21862306a36Sopenharmony_ci	 * We ignore any "ec" data since it might be in the head or
21962306a36Sopenharmony_ci	 * the tail, and we really don't need to deal with it.
22062306a36Sopenharmony_ci	 * Note that buf->head[0].iov_len may indicate the available
22162306a36Sopenharmony_ci	 * head buffer space rather than that actually occupied.
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	movelen = min_t(unsigned int, buf->head[0].iov_len, len);
22462306a36Sopenharmony_ci	movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
22562306a36Sopenharmony_ci	BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
22662306a36Sopenharmony_ci							buf->head[0].iov_len);
22762306a36Sopenharmony_ci	memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
22862306a36Sopenharmony_ci	buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
22962306a36Sopenharmony_ci	buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Trim off the trailing "extra count" and checksum blob */
23262306a36Sopenharmony_ci	xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	*align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);
23562306a36Sopenharmony_ci	*slack = *align + XDR_QUADLEN(ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
23662306a36Sopenharmony_ci	return GSS_S_COMPLETE;
23762306a36Sopenharmony_ci}
238