xref: /linux/net/sunrpc/auth_gss/gss_krb5_crypto.c (revision 979accbc6bcb551b095b678a6f0c41899080ccd1)
1 /*
2  *  linux/net/sunrpc/gss_krb5_crypto.c
3  *
4  *  Copyright (c) 2000-2008 The Regents of the University of Michigan.
5  *  All rights reserved.
6  *
7  *  Andy Adamson   <andros@umich.edu>
8  *  Bruce Fields   <bfields@umich.edu>
9  */
10 
11 /*
12  * Copyright (C) 1998 by the FundsXpress, INC.
13  *
14  * All rights reserved.
15  *
16  * Export of this software from the United States of America may require
17  * a specific license from the United States Government.  It is the
18  * responsibility of any person or organization contemplating export to
19  * obtain such a license before exporting.
20  *
21  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
22  * distribute this software and its documentation for any purpose and
23  * without fee is hereby granted, provided that the above copyright
24  * notice appear in all copies and that both that copyright notice and
25  * this permission notice appear in supporting documentation, and that
26  * the name of FundsXpress. not be used in advertising or publicity pertaining
27  * to distribution of the software without specific, written prior
28  * permission.  FundsXpress makes no representations about the suitability of
29  * this software for any purpose.  It is provided "as is" without express
30  * or implied warranty.
31  *
32  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
33  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
34  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
35  */
36 
37 #include <linux/err.h>
38 #include <linux/types.h>
39 #include <linux/mm.h>
40 #include <linux/scatterlist.h>
41 #include <linux/highmem.h>
42 #include <linux/pagemap.h>
43 #include <linux/sunrpc/gss_krb5.h>
44 #include <linux/sunrpc/xdr.h>
45 
46 #include "gss_krb5_internal.h"
47 
48 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
49 # define RPCDBG_FACILITY        RPCDBG_AUTH
50 #endif
51 
52 
53 /*
54  * This function makes the assumption that it was ultimately called
55  * from gss_wrap().
56  *
57  * The client auth_gss code moves any existing tail data into a
58  * separate page before calling gss_wrap.
59  * The server svcauth_gss code ensures that both the head and the
60  * tail have slack space of RPC_MAX_AUTH_SIZE before calling gss_wrap.
61  *
62  * Even with that guarantee, this function may be called more than
63  * once in the processing of gss_wrap().  The best we can do is
64  * verify at compile-time (see GSS_KRB5_MAX_SLACK_NEEDED) that the
65  * largest expected shift will fit within RPC_MAX_AUTH_SIZE.
66  * At run-time we can verify that a single invocation of this
67  * function doesn't attempt to use more the RPC_MAX_AUTH_SIZE.
68  */
69 
70 int
71 xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)
72 {
73 	u8 *p;
74 
75 	if (shiftlen == 0)
76 		return 0;
77 
78 	BUG_ON(shiftlen > RPC_MAX_AUTH_SIZE);
79 
80 	p = buf->head[0].iov_base + base;
81 
82 	memmove(p + shiftlen, p, buf->head[0].iov_len - base);
83 
84 	buf->head[0].iov_len += shiftlen;
85 	buf->len += shiftlen;
86 
87 	return 0;
88 }
89 
90 
91 /**
92  * gss_krb5_aead_encrypt - Encrypt a wrap token using crypto/krb5
93  * @kctx: Kerberos context
94  * @offset: byte offset of the GSS token header in @buf
95  * @buf: OUT: send buffer
96  * @pages: plaintext payload pages (page cache data)
97  *
98  * The xdr_buf setup mirrors the original per-enctype encrypt
99  * functions, but the CBC-CTS encryption and HMAC are replaced
100  * by a single AEAD operation through the crypto/krb5 library.
101  *
102  * Return values:
103  *   %GSS_S_COMPLETE: Encryption successful
104  *   %GSS_S_FAILURE: Encryption failed
105  */
106 u32
107 gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
108 		      struct xdr_buf *buf, struct page **pages)
109 {
110 	const struct krb5_enctype *krb5 = kctx->krb5e;
111 	struct crypto_aead *aead = kctx->initiate ?
112 		kctx->initiator_enc_aead : kctx->acceptor_enc_aead;
113 	unsigned int conflen = krb5->conf_len;
114 	unsigned int cksum_len = krb5->cksum_len;
115 	unsigned int sec_offset, sec_len, data_len;
116 	struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
117 	struct scatterlist *sg_overflow = NULL;
118 	ssize_t ret;
119 	int nsg;
120 
121 	/* Insert space for the confounder */
122 	if (xdr_extend_head(buf, offset + GSS_KRB5_TOK_HDR_LEN, conflen))
123 		return GSS_S_FAILURE;
124 
125 	/* Ensure a tail segment exists */
126 	if (buf->tail[0].iov_base == NULL) {
127 		buf->tail[0].iov_base = buf->head[0].iov_base
128 						+ buf->head[0].iov_len;
129 		buf->tail[0].iov_len = 0;
130 	}
131 
132 	/* Append a copy of the plaintext GSS token header (RFC 4121 Sec 4.2.4) */
133 	memcpy(buf->tail[0].iov_base + buf->tail[0].iov_len,
134 	       buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
135 	buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
136 	buf->len += GSS_KRB5_TOK_HDR_LEN;
137 
138 	/* Reserve space for the integrity checksum */
139 	buf->tail[0].iov_len += cksum_len;
140 	buf->len += cksum_len;
141 
142 	/*
143 	 * The AEAD operates in-place, but on the client send path the
144 	 * plaintext payload lives in page cache pages that must not be
145 	 * modified.  Copy the payload into the scratch output pages
146 	 * first.  On the server send path @pages and buf->pages are
147 	 * the same array, and no copy is needed.
148 	 *
149 	 * Both arrays share buf->page_base, so the same index and
150 	 * intra-page offset address corresponding data in each.
151 	 */
152 	if (pages != buf->pages) {
153 		unsigned int poff = buf->page_base;
154 		unsigned int plen = buf->page_len;
155 		unsigned int i = poff >> PAGE_SHIFT;
156 		unsigned int off = offset_in_page(poff);
157 
158 		while (plen) {
159 			unsigned int n = min_t(unsigned int, plen,
160 					       PAGE_SIZE - off);
161 			memcpy_page(buf->pages[i], off, pages[i], off, n);
162 			plen -= n;
163 			i++;
164 			off = 0;
165 		}
166 	}
167 
168 	/* Build scatterlist covering the secured region */
169 	sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
170 	sec_len = buf->len - sec_offset;
171 	data_len = sec_len - conflen - cksum_len;
172 
173 	nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
174 				  sg, ARRAY_SIZE(sg),
175 				  &sg_overflow, GFP_NOFS);
176 	if (nsg < 0)
177 		return GSS_S_FAILURE;
178 
179 	ret = crypto_krb5_encrypt(krb5, aead, sg, nsg, sec_len,
180 				  conflen, data_len, false);
181 	kfree(sg_overflow);
182 	if (ret < 0)
183 		return GSS_S_FAILURE;
184 
185 	return GSS_S_COMPLETE;
186 }
187 
188 /**
189  * gss_krb5_aead_decrypt - Decrypt a wrap token using crypto/krb5
190  * @kctx: Kerberos context
191  * @offset: byte offset of the GSS token header in @buf
192  * @len: total length of the GSS token
193  * @buf: ciphertext buffer, decrypted in-place
194  * @headskip: OUT: confounder length, in octets
195  * @tailskip: OUT: checksum length, in octets
196  *
197  * Return values:
198  *   %GSS_S_COMPLETE: Decryption and integrity verification succeeded
199  *   %GSS_S_BAD_SIG: Integrity checksum did not match
200  *   %GSS_S_DEFECTIVE_TOKEN: Token is malformed or truncated
201  *   %GSS_S_FAILURE: Decryption failed
202  */
203 u32
204 gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
205 		      struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
206 {
207 	const struct krb5_enctype *krb5 = kctx->krb5e;
208 	struct crypto_aead *aead = kctx->initiate ?
209 		kctx->acceptor_enc_aead : kctx->initiator_enc_aead;
210 	unsigned int sec_offset, sec_len;
211 	size_t data_offset, data_len;
212 	struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
213 	struct scatterlist *sg_overflow = NULL;
214 	int nsg, ret;
215 
216 	/* Secured region starts after the GSS token header */
217 	sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
218 	if (len < sec_offset)
219 		return GSS_S_DEFECTIVE_TOKEN;
220 	sec_len = len - sec_offset;
221 
222 	nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
223 				  sg, ARRAY_SIZE(sg),
224 				  &sg_overflow, GFP_NOFS);
225 	if (nsg < 0)
226 		return GSS_S_FAILURE;
227 
228 	data_offset = 0;
229 	data_len = sec_len;
230 	ret = crypto_krb5_decrypt(krb5, aead, sg, nsg,
231 				  &data_offset, &data_len);
232 	kfree(sg_overflow);
233 	if (ret < 0)
234 		return gss_krb5_errno_to_status(ret);
235 
236 	*headskip = data_offset;
237 	*tailskip = sec_len - data_offset - data_len;
238 	return GSS_S_COMPLETE;
239 }
240 
241 /**
242  * gss_krb5_mic_build_sg - Build scatterlist for MIC token operations
243  * @body: xdr_buf containing the message body
244  * @cksum: pointer to checksum area in the token buffer
245  * @cksum_len: length of checksum area
246  * @hdr: pointer to GSS token header
247  * @sg_head: caller-provided scatterlist array; if more than
248  *	XDR_BUF_TO_SG_NENTS entries are needed, an overflow
249  *	scatterlist is allocated and chained automatically
250  * @sg_overflow: OUT: overflow scatterlist, caller must kfree
251  *
252  * Per RFC 4121 Section 4.2.4, MIC token checksums cover the
253  * message body followed by the token header. The checksum
254  * output or received checksum occupies the first scatterlist
255  * entry.  This layout cannot be constructed by
256  * xdr_buf_to_sg_alloc() because the checksum area and the GSS
257  * header lie outside the xdr_buf.
258  *
259  * Returns the number of scatterlist entries on success, or a
260  * negative errno on failure.
261  */
262 int gss_krb5_mic_build_sg(const struct xdr_buf *body,
263 			  void *cksum, unsigned int cksum_len,
264 			  void *hdr,
265 			  struct scatterlist *sg_head,
266 			  struct scatterlist **sg_overflow)
267 {
268 	struct scatterlist *entry;
269 	int body_max, body_nsg, nsg;
270 
271 	*sg_overflow = NULL;
272 
273 	body_max = 2;
274 	if (body->page_len)
275 		body_max += DIV_ROUND_UP(body->page_len +
276 					 offset_in_page(body->page_base),
277 					 PAGE_SIZE);
278 	nsg = 1 + body_max + 1;
279 	if (nsg <= XDR_BUF_TO_SG_NENTS) {
280 		sg_init_table(sg_head, nsg);
281 	} else {
282 		unsigned int overflow_nents =
283 			nsg - XDR_BUF_TO_SG_NENTS + 1;
284 
285 		*sg_overflow = kmalloc_array(overflow_nents,
286 					     sizeof(**sg_overflow),
287 					     GFP_NOFS);
288 		if (!*sg_overflow)
289 			return -ENOMEM;
290 
291 		sg_init_table(sg_head, XDR_BUF_TO_SG_NENTS);
292 		sg_init_table(*sg_overflow, overflow_nents);
293 		sg_chain(sg_head, XDR_BUF_TO_SG_NENTS, *sg_overflow);
294 	}
295 
296 	sg_set_buf(&sg_head[0], cksum, cksum_len);
297 	body_nsg = xdr_buf_to_sg(body, 0, body->len,
298 				 sg_next(&sg_head[0]), body_max);
299 	if (body_nsg < 0)
300 		goto out_err;
301 
302 	/*
303 	 * xdr_buf_to_sg marks the last body entry as end-of-list;
304 	 * clear it so the trailing header entry is reachable.
305 	 */
306 	if (body_nsg > 0) {
307 		entry = sg_last(sg_next(&sg_head[0]), body_nsg);
308 		sg_unmark_end(entry);
309 		entry = sg_next(entry);
310 	} else {
311 		entry = sg_next(&sg_head[0]);
312 	}
313 	sg_set_buf(entry, hdr, GSS_KRB5_TOK_HDR_LEN);
314 	sg_mark_end(entry);
315 	return 1 + body_nsg + 1;
316 
317 out_err:
318 	kfree(*sg_overflow);
319 	*sg_overflow = NULL;
320 	return body_nsg;
321 }
322