xref: /linux/net/sunrpc/auth_gss/gss_krb5_wrap.c (revision d141ec2825b4d3ec52f27c43bdd864090159273a)
1 /*
2  * COPYRIGHT (c) 2008
3  * The Regents of the University of Michigan
4  * ALL RIGHTS RESERVED
5  *
6  * Permission is granted to use, copy, create derivative works
7  * and redistribute this software and such derivative works
8  * for any purpose, so long as the name of The University of
9  * Michigan is not used in any advertising or publicity
10  * pertaining to the use of distribution of this software
11  * without specific, written prior authorization.  If the
12  * above copyright notice or any other identification of the
13  * University of Michigan is included in any copy of any
14  * portion of this software, then the disclaimer below must
15  * also be included.
16  *
17  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
18  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
19  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
20  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
21  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
23  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
25  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
26  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
27  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGES.
29  */
30 
31 #include <linux/types.h>
32 #include <linux/jiffies.h>
33 #include <linux/sunrpc/gss_krb5.h>
34 #include <linux/pagemap.h>
35 
36 #include "gss_krb5_internal.h"
37 
38 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
39 # define RPCDBG_FACILITY	RPCDBG_AUTH
40 #endif
41 
42 /*
43  * We can shift data by up to LOCAL_BUF_LEN bytes in a pass.  If we need
44  * to do more than that, we shift repeatedly.  Kevin Coffman reports
45  * seeing 28 bytes as the value used by Microsoft clients and servers
46  * with AES, so this constant is chosen to allow handling 28 in one pass
47  * without using too much stack space.
48  *
49  * If that proves to a problem perhaps we could use a more clever
50  * algorithm.
51  */
52 #define LOCAL_BUF_LEN 32u
53 
rotate_buf_a_little(struct xdr_buf * buf,unsigned int shift)54 static void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
55 {
56 	char head[LOCAL_BUF_LEN];
57 	char tmp[LOCAL_BUF_LEN];
58 	unsigned int this_len, i;
59 
60 	BUG_ON(shift > LOCAL_BUF_LEN);
61 
62 	read_bytes_from_xdr_buf(buf, 0, head, shift);
63 	for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
64 		this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
65 		read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
66 		write_bytes_to_xdr_buf(buf, i, tmp, this_len);
67 	}
68 	write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
69 }
70 
_rotate_left(struct xdr_buf * buf,unsigned int shift)71 static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
72 {
73 	int shifted = 0;
74 	int this_shift;
75 
76 	if (!buf->len)
77 		return;
78 	shift %= buf->len;
79 	while (shifted < shift) {
80 		this_shift = min(shift - shifted, LOCAL_BUF_LEN);
81 		rotate_buf_a_little(buf, this_shift);
82 		shifted += this_shift;
83 	}
84 }
85 
rotate_left(u32 base,struct xdr_buf * buf,unsigned int shift)86 static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
87 {
88 	struct xdr_buf subbuf;
89 
90 	if (buf->len <= base)
91 		return;
92 	xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
93 	_rotate_left(&subbuf, shift);
94 }
95 
96 u32
gss_krb5_wrap_v2(struct krb5_ctx * kctx,int offset,struct xdr_buf * buf,struct page ** pages)97 gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
98 		 struct xdr_buf *buf, struct page **pages)
99 {
100 	u8		*ptr;
101 	time64_t	now;
102 	u8		flags = 0x00;
103 	__be16		*be16ptr;
104 	__be64		*be64ptr;
105 	u32		err;
106 
107 	dprintk("RPC:       %s\n", __func__);
108 
109 	/* make room for gss token header */
110 	if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN))
111 		return GSS_S_FAILURE;
112 
113 	/* construct gss token header */
114 	ptr = buf->head[0].iov_base + offset;
115 	*ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
116 	*ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
117 
118 	if (!kctx->initiate)
119 		flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
120 	if (kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
121 		flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
122 	/* We always do confidentiality in wrap tokens */
123 	flags |= KG2_TOKEN_FLAG_SEALED;
124 
125 	*ptr++ = flags;
126 	*ptr++ = 0xff;
127 	be16ptr = (__be16 *)ptr;
128 
129 	*be16ptr++ = 0;
130 	/* "inner" token header always uses 0 for RRC */
131 	*be16ptr++ = 0;
132 
133 	be64ptr = (__be64 *)be16ptr;
134 	*be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
135 
136 	err = gss_krb5_aead_encrypt(kctx, offset, buf, pages);
137 	if (err)
138 		return err;
139 
140 	now = ktime_get_real_seconds();
141 	return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
142 }
143 
144 u32
gss_krb5_unwrap_v2(struct krb5_ctx * kctx,int offset,int len,struct xdr_buf * buf,unsigned int * slack,unsigned int * align)145 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
146 		   struct xdr_buf *buf, unsigned int *slack,
147 		   unsigned int *align)
148 {
149 	time64_t	now;
150 	u8		*ptr;
151 	u8		flags = 0x00;
152 	u16		ec, rrc;
153 	int		err;
154 	u32		headskip, tailskip;
155 	u8		decrypted_hdr[GSS_KRB5_TOK_HDR_LEN];
156 	unsigned int	movelen;
157 
158 
159 	dprintk("RPC:       %s\n", __func__);
160 
161 	if (len - offset <= GSS_KRB5_TOK_HDR_LEN)
162 		return GSS_S_DEFECTIVE_TOKEN;
163 
164 	ptr = buf->head[0].iov_base + offset;
165 
166 	if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
167 		return GSS_S_DEFECTIVE_TOKEN;
168 
169 	flags = ptr[2];
170 	if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
171 	    (kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
172 		return GSS_S_BAD_SIG;
173 
174 	if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) {
175 		dprintk("%s: token missing expected sealed flag\n", __func__);
176 		return GSS_S_DEFECTIVE_TOKEN;
177 	}
178 
179 	if (ptr[3] != 0xff)
180 		return GSS_S_DEFECTIVE_TOKEN;
181 
182 	ec = be16_to_cpup((__be16 *)(ptr + 4));
183 	rrc = be16_to_cpup((__be16 *)(ptr + 6));
184 
185 	/*
186 	 * NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
187 	 * doesn't want it checked; see page 6 of rfc 2203.
188 	 */
189 
190 	if (rrc != 0)
191 		rotate_left(offset + 16, buf, rrc);
192 
193 	err = gss_krb5_aead_decrypt(kctx, offset, len, buf,
194 				    &headskip, &tailskip);
195 	if (err)
196 		return err;
197 
198 	/*
199 	 * Retrieve the decrypted gss token header and verify
200 	 * it against the original
201 	 */
202 	err = read_bytes_from_xdr_buf(buf,
203 				len - GSS_KRB5_TOK_HDR_LEN - tailskip,
204 				decrypted_hdr, GSS_KRB5_TOK_HDR_LEN);
205 	if (err) {
206 		dprintk("%s: error %u getting decrypted_hdr\n", __func__, err);
207 		return GSS_S_FAILURE;
208 	}
209 	if (memcmp(ptr, decrypted_hdr, 6)
210 				|| memcmp(ptr + 8, decrypted_hdr + 8, 8)) {
211 		dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__);
212 		return GSS_S_FAILURE;
213 	}
214 
215 	/* do sequencing checks */
216 
217 	/* it got through unscathed.  Make sure the context is unexpired */
218 	now = ktime_get_real_seconds();
219 	if (now > kctx->endtime)
220 		return GSS_S_CONTEXT_EXPIRED;
221 
222 	/*
223 	 * Move the head data back to the right position in xdr_buf.
224 	 * We ignore any "ec" data since it might be in the head or
225 	 * the tail, and we really don't need to deal with it.
226 	 * Note that buf->head[0].iov_len may indicate the available
227 	 * head buffer space rather than that actually occupied.
228 	 */
229 	movelen = min_t(unsigned int, buf->head[0].iov_len, len);
230 	if (movelen < offset + GSS_KRB5_TOK_HDR_LEN + headskip)
231 		return GSS_S_DEFECTIVE_TOKEN;
232 	movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
233 	memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
234 	buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
235 	buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
236 
237 	/* Trim off the trailing "extra count" and checksum blob */
238 	if (ec + GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset)
239 		return GSS_S_DEFECTIVE_TOKEN;
240 	xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
241 
242 	*align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);
243 	*slack = *align + XDR_QUADLEN(ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
244 	return GSS_S_COMPLETE;
245 }
246