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 <crypto/skcipher.h>
32#include <linux/types.h>
33#include <linux/jiffies.h>
34#include <linux/sunrpc/gss_krb5.h>
35#include <linux/pagemap.h>
36
37#include "gss_krb5_internal.h"
38
39#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
40# define RPCDBG_FACILITY RPCDBG_AUTH
41#endif
42
43/*
44 * We can shift data by up to LOCAL_BUF_LEN bytes in a pass. If we need
45 * to do more than that, we shift repeatedly. Kevin Coffman reports
46 * seeing 28 bytes as the value used by Microsoft clients and servers
47 * with AES, so this constant is chosen to allow handling 28 in one pass
48 * without using too much stack space.
49 *
50 * If that proves to a problem perhaps we could use a more clever
51 * algorithm.
52 */
53#define LOCAL_BUF_LEN 32u
54
55static void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
56{
57 char head[LOCAL_BUF_LEN];
58 char tmp[LOCAL_BUF_LEN];
59 unsigned int this_len, i;
60
61 BUG_ON(shift > LOCAL_BUF_LEN);
62
63 read_bytes_from_xdr_buf(buf, 0, head, shift);
64 for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
65 this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
66 read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
67 write_bytes_to_xdr_buf(buf, i, tmp, this_len);
68 }
69 write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
70}
71
72static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
73{
74 int shifted = 0;
75 int this_shift;
76
77 shift %= buf->len;
78 while (shifted < shift) {
79 this_shift = min(shift - shifted, LOCAL_BUF_LEN);
80 rotate_buf_a_little(buf, shift: this_shift);
81 shifted += this_shift;
82 }
83}
84
85static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
86{
87 struct xdr_buf subbuf;
88
89 xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
90 _rotate_left(buf: &subbuf, shift);
91}
92
93u32
94gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
95 struct xdr_buf *buf, struct page **pages)
96{
97 u8 *ptr;
98 time64_t now;
99 u8 flags = 0x00;
100 __be16 *be16ptr;
101 __be64 *be64ptr;
102 u32 err;
103
104 dprintk("RPC: %s\n", __func__);
105
106 /* make room for gss token header */
107 if (xdr_extend_head(buf, base: offset, GSS_KRB5_TOK_HDR_LEN))
108 return GSS_S_FAILURE;
109
110 /* construct gss token header */
111 ptr = buf->head[0].iov_base + offset;
112 *ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
113 *ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
114
115 if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
116 flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
117 if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
118 flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
119 /* We always do confidentiality in wrap tokens */
120 flags |= KG2_TOKEN_FLAG_SEALED;
121
122 *ptr++ = flags;
123 *ptr++ = 0xff;
124 be16ptr = (__be16 *)ptr;
125
126 *be16ptr++ = 0;
127 /* "inner" token header always uses 0 for RRC */
128 *be16ptr++ = 0;
129
130 be64ptr = (__be64 *)be16ptr;
131 *be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
132
133 err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages);
134 if (err)
135 return err;
136
137 now = ktime_get_real_seconds();
138 return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
139}
140
141u32
142gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
143 struct xdr_buf *buf, unsigned int *slack,
144 unsigned int *align)
145{
146 time64_t now;
147 u8 *ptr;
148 u8 flags = 0x00;
149 u16 ec, rrc;
150 int err;
151 u32 headskip, tailskip;
152 u8 decrypted_hdr[GSS_KRB5_TOK_HDR_LEN];
153 unsigned int movelen;
154
155
156 dprintk("RPC: %s\n", __func__);
157
158 ptr = buf->head[0].iov_base + offset;
159
160 if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
161 return GSS_S_DEFECTIVE_TOKEN;
162
163 flags = ptr[2];
164 if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
165 (kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
166 return GSS_S_BAD_SIG;
167
168 if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) {
169 dprintk("%s: token missing expected sealed flag\n", __func__);
170 return GSS_S_DEFECTIVE_TOKEN;
171 }
172
173 if (ptr[3] != 0xff)
174 return GSS_S_DEFECTIVE_TOKEN;
175
176 ec = be16_to_cpup(p: (__be16 *)(ptr + 4));
177 rrc = be16_to_cpup(p: (__be16 *)(ptr + 6));
178
179 /*
180 * NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
181 * doesn't want it checked; see page 6 of rfc 2203.
182 */
183
184 if (rrc != 0)
185 rotate_left(base: offset + 16, buf, shift: rrc);
186
187 err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf,
188 &headskip, &tailskip);
189 if (err)
190 return GSS_S_FAILURE;
191
192 /*
193 * Retrieve the decrypted gss token header and verify
194 * it against the original
195 */
196 err = read_bytes_from_xdr_buf(buf,
197 len - GSS_KRB5_TOK_HDR_LEN - tailskip,
198 decrypted_hdr, GSS_KRB5_TOK_HDR_LEN);
199 if (err) {
200 dprintk("%s: error %u getting decrypted_hdr\n", __func__, err);
201 return GSS_S_FAILURE;
202 }
203 if (memcmp(p: ptr, q: decrypted_hdr, size: 6)
204 || memcmp(p: ptr + 8, q: decrypted_hdr + 8, size: 8)) {
205 dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__);
206 return GSS_S_FAILURE;
207 }
208
209 /* do sequencing checks */
210
211 /* it got through unscathed. Make sure the context is unexpired */
212 now = ktime_get_real_seconds();
213 if (now > kctx->endtime)
214 return GSS_S_CONTEXT_EXPIRED;
215
216 /*
217 * Move the head data back to the right position in xdr_buf.
218 * We ignore any "ec" data since it might be in the head or
219 * the tail, and we really don't need to deal with it.
220 * Note that buf->head[0].iov_len may indicate the available
221 * head buffer space rather than that actually occupied.
222 */
223 movelen = min_t(unsigned int, buf->head[0].iov_len, len);
224 movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
225 BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
226 buf->head[0].iov_len);
227 memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
228 buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
229 buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
230
231 /* Trim off the trailing "extra count" and checksum blob */
232 xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
233
234 *align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);
235 *slack = *align + XDR_QUADLEN(ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
236 return GSS_S_COMPLETE;
237}
238

source code of linux/net/sunrpc/auth_gss/gss_krb5_wrap.c