xref: /linux/net/sunrpc/auth_gss/gss_krb5_crypto.c (revision f24e9f586b377749dff37554696cf3a105540c94)
1 /*
2  *  linux/net/sunrpc/gss_krb5_crypto.c
3  *
4  *  Copyright (c) 2000 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/slab.h>
41 #include <linux/scatterlist.h>
42 #include <linux/crypto.h>
43 #include <linux/highmem.h>
44 #include <linux/pagemap.h>
45 #include <linux/sunrpc/gss_krb5.h>
46 
47 #ifdef RPC_DEBUG
48 # define RPCDBG_FACILITY        RPCDBG_AUTH
49 #endif
50 
51 u32
52 krb5_encrypt(
53 	struct crypto_blkcipher *tfm,
54 	void * iv,
55 	void * in,
56 	void * out,
57 	int length)
58 {
59 	u32 ret = -EINVAL;
60         struct scatterlist sg[1];
61 	u8 local_iv[16] = {0};
62 	struct blkcipher_desc desc = { .tfm = tfm, .info = local_iv };
63 
64 	dprintk("RPC:      krb5_encrypt: input data:\n");
65 	print_hexl((u32 *)in, length, 0);
66 
67 	if (length % crypto_blkcipher_blocksize(tfm) != 0)
68 		goto out;
69 
70 	if (crypto_blkcipher_ivsize(tfm) > 16) {
71 		dprintk("RPC:      gss_k5encrypt: tfm iv size to large %d\n",
72 		         crypto_blkcipher_ivsize(tfm));
73 		goto out;
74 	}
75 
76 	if (iv)
77 		memcpy(local_iv, iv, crypto_blkcipher_ivsize(tfm));
78 
79 	memcpy(out, in, length);
80 	sg_set_buf(sg, out, length);
81 
82 	ret = crypto_blkcipher_encrypt_iv(&desc, sg, sg, length);
83 
84 	dprintk("RPC:      krb5_encrypt: output data:\n");
85 	print_hexl((u32 *)out, length, 0);
86 out:
87 	dprintk("RPC:      krb5_encrypt returns %d\n",ret);
88 	return(ret);
89 }
90 
91 EXPORT_SYMBOL(krb5_encrypt);
92 
93 u32
94 krb5_decrypt(
95      struct crypto_blkcipher *tfm,
96      void * iv,
97      void * in,
98      void * out,
99      int length)
100 {
101 	u32 ret = -EINVAL;
102 	struct scatterlist sg[1];
103 	u8 local_iv[16] = {0};
104 	struct blkcipher_desc desc = { .tfm = tfm, .info = local_iv };
105 
106 	dprintk("RPC:      krb5_decrypt: input data:\n");
107 	print_hexl((u32 *)in, length, 0);
108 
109 	if (length % crypto_blkcipher_blocksize(tfm) != 0)
110 		goto out;
111 
112 	if (crypto_blkcipher_ivsize(tfm) > 16) {
113 		dprintk("RPC:      gss_k5decrypt: tfm iv size to large %d\n",
114 			crypto_blkcipher_ivsize(tfm));
115 		goto out;
116 	}
117 	if (iv)
118 		memcpy(local_iv,iv, crypto_blkcipher_ivsize(tfm));
119 
120 	memcpy(out, in, length);
121 	sg_set_buf(sg, out, length);
122 
123 	ret = crypto_blkcipher_decrypt_iv(&desc, sg, sg, length);
124 
125 	dprintk("RPC:      krb5_decrypt: output_data:\n");
126 	print_hexl((u32 *)out, length, 0);
127 out:
128 	dprintk("RPC:      gss_k5decrypt returns %d\n",ret);
129 	return(ret);
130 }
131 
132 EXPORT_SYMBOL(krb5_decrypt);
133 
134 static int
135 process_xdr_buf(struct xdr_buf *buf, int offset, int len,
136 		int (*actor)(struct scatterlist *, void *), void *data)
137 {
138 	int i, page_len, thislen, page_offset, ret = 0;
139 	struct scatterlist	sg[1];
140 
141 	if (offset >= buf->head[0].iov_len) {
142 		offset -= buf->head[0].iov_len;
143 	} else {
144 		thislen = buf->head[0].iov_len - offset;
145 		if (thislen > len)
146 			thislen = len;
147 		sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
148 		ret = actor(sg, data);
149 		if (ret)
150 			goto out;
151 		offset = 0;
152 		len -= thislen;
153 	}
154 	if (len == 0)
155 		goto out;
156 
157 	if (offset >= buf->page_len) {
158 		offset -= buf->page_len;
159 	} else {
160 		page_len = buf->page_len - offset;
161 		if (page_len > len)
162 			page_len = len;
163 		len -= page_len;
164 		page_offset = (offset + buf->page_base) & (PAGE_CACHE_SIZE - 1);
165 		i = (offset + buf->page_base) >> PAGE_CACHE_SHIFT;
166 		thislen = PAGE_CACHE_SIZE - page_offset;
167 		do {
168 			if (thislen > page_len)
169 				thislen = page_len;
170 			sg->page = buf->pages[i];
171 			sg->offset = page_offset;
172 			sg->length = thislen;
173 			ret = actor(sg, data);
174 			if (ret)
175 				goto out;
176 			page_len -= thislen;
177 			i++;
178 			page_offset = 0;
179 			thislen = PAGE_CACHE_SIZE;
180 		} while (page_len != 0);
181 		offset = 0;
182 	}
183 	if (len == 0)
184 		goto out;
185 
186 	if (offset < buf->tail[0].iov_len) {
187 		thislen = buf->tail[0].iov_len - offset;
188 		if (thislen > len)
189 			thislen = len;
190 		sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
191 		ret = actor(sg, data);
192 		len -= thislen;
193 	}
194 	if (len != 0)
195 		ret = -EINVAL;
196 out:
197 	return ret;
198 }
199 
200 static int
201 checksummer(struct scatterlist *sg, void *data)
202 {
203 	struct hash_desc *desc = data;
204 
205 	return crypto_hash_update(desc, sg, sg->length);
206 }
207 
208 /* checksum the plaintext data and hdrlen bytes of the token header */
209 s32
210 make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body,
211 		   int body_offset, struct xdr_netobj *cksum)
212 {
213 	char                            *cksumname;
214 	struct hash_desc                desc; /* XXX add to ctx? */
215 	struct scatterlist              sg[1];
216 	int err;
217 
218 	switch (cksumtype) {
219 		case CKSUMTYPE_RSA_MD5:
220 			cksumname = "md5";
221 			break;
222 		default:
223 			dprintk("RPC:      krb5_make_checksum:"
224 				" unsupported checksum %d", cksumtype);
225 			return GSS_S_FAILURE;
226 	}
227 	desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC);
228 	if (IS_ERR(desc.tfm))
229 		return GSS_S_FAILURE;
230 	cksum->len = crypto_hash_digestsize(desc.tfm);
231 	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
232 
233 	err = crypto_hash_init(&desc);
234 	if (err)
235 		goto out;
236 	sg_set_buf(sg, header, hdrlen);
237 	err = crypto_hash_update(&desc, sg, hdrlen);
238 	if (err)
239 		goto out;
240 	err = process_xdr_buf(body, body_offset, body->len - body_offset,
241 			      checksummer, &desc);
242 	if (err)
243 		goto out;
244 	err = crypto_hash_final(&desc, cksum->data);
245 
246 out:
247 	crypto_free_hash(desc.tfm);
248 	return err ? GSS_S_FAILURE : 0;
249 }
250 
251 EXPORT_SYMBOL(make_checksum);
252 
253 struct encryptor_desc {
254 	u8 iv[8]; /* XXX hard-coded blocksize */
255 	struct blkcipher_desc desc;
256 	int pos;
257 	struct xdr_buf *outbuf;
258 	struct page **pages;
259 	struct scatterlist infrags[4];
260 	struct scatterlist outfrags[4];
261 	int fragno;
262 	int fraglen;
263 };
264 
265 static int
266 encryptor(struct scatterlist *sg, void *data)
267 {
268 	struct encryptor_desc *desc = data;
269 	struct xdr_buf *outbuf = desc->outbuf;
270 	struct page *in_page;
271 	int thislen = desc->fraglen + sg->length;
272 	int fraglen, ret;
273 	int page_pos;
274 
275 	/* Worst case is 4 fragments: head, end of page 1, start
276 	 * of page 2, tail.  Anything more is a bug. */
277 	BUG_ON(desc->fragno > 3);
278 	desc->infrags[desc->fragno] = *sg;
279 	desc->outfrags[desc->fragno] = *sg;
280 
281 	page_pos = desc->pos - outbuf->head[0].iov_len;
282 	if (page_pos >= 0 && page_pos < outbuf->page_len) {
283 		/* pages are not in place: */
284 		int i = (page_pos + outbuf->page_base) >> PAGE_CACHE_SHIFT;
285 		in_page = desc->pages[i];
286 	} else {
287 		in_page = sg->page;
288 	}
289 	desc->infrags[desc->fragno].page = in_page;
290 	desc->fragno++;
291 	desc->fraglen += sg->length;
292 	desc->pos += sg->length;
293 
294 	fraglen = thislen & 7; /* XXX hardcoded blocksize */
295 	thislen -= fraglen;
296 
297 	if (thislen == 0)
298 		return 0;
299 
300 	ret = crypto_blkcipher_encrypt_iv(&desc->desc, desc->outfrags,
301 					  desc->infrags, thislen);
302 	if (ret)
303 		return ret;
304 	if (fraglen) {
305 		desc->outfrags[0].page = sg->page;
306 		desc->outfrags[0].offset = sg->offset + sg->length - fraglen;
307 		desc->outfrags[0].length = fraglen;
308 		desc->infrags[0] = desc->outfrags[0];
309 		desc->infrags[0].page = in_page;
310 		desc->fragno = 1;
311 		desc->fraglen = fraglen;
312 	} else {
313 		desc->fragno = 0;
314 		desc->fraglen = 0;
315 	}
316 	return 0;
317 }
318 
319 int
320 gss_encrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
321 		    int offset, struct page **pages)
322 {
323 	int ret;
324 	struct encryptor_desc desc;
325 
326 	BUG_ON((buf->len - offset) % crypto_blkcipher_blocksize(tfm) != 0);
327 
328 	memset(desc.iv, 0, sizeof(desc.iv));
329 	desc.desc.tfm = tfm;
330 	desc.desc.info = desc.iv;
331 	desc.desc.flags = 0;
332 	desc.pos = offset;
333 	desc.outbuf = buf;
334 	desc.pages = pages;
335 	desc.fragno = 0;
336 	desc.fraglen = 0;
337 
338 	ret = process_xdr_buf(buf, offset, buf->len - offset, encryptor, &desc);
339 	return ret;
340 }
341 
342 EXPORT_SYMBOL(gss_encrypt_xdr_buf);
343 
344 struct decryptor_desc {
345 	u8 iv[8]; /* XXX hard-coded blocksize */
346 	struct blkcipher_desc desc;
347 	struct scatterlist frags[4];
348 	int fragno;
349 	int fraglen;
350 };
351 
352 static int
353 decryptor(struct scatterlist *sg, void *data)
354 {
355 	struct decryptor_desc *desc = data;
356 	int thislen = desc->fraglen + sg->length;
357 	int fraglen, ret;
358 
359 	/* Worst case is 4 fragments: head, end of page 1, start
360 	 * of page 2, tail.  Anything more is a bug. */
361 	BUG_ON(desc->fragno > 3);
362 	desc->frags[desc->fragno] = *sg;
363 	desc->fragno++;
364 	desc->fraglen += sg->length;
365 
366 	fraglen = thislen & 7; /* XXX hardcoded blocksize */
367 	thislen -= fraglen;
368 
369 	if (thislen == 0)
370 		return 0;
371 
372 	ret = crypto_blkcipher_decrypt_iv(&desc->desc, desc->frags,
373 					  desc->frags, thislen);
374 	if (ret)
375 		return ret;
376 	if (fraglen) {
377 		desc->frags[0].page = sg->page;
378 		desc->frags[0].offset = sg->offset + sg->length - fraglen;
379 		desc->frags[0].length = fraglen;
380 		desc->fragno = 1;
381 		desc->fraglen = fraglen;
382 	} else {
383 		desc->fragno = 0;
384 		desc->fraglen = 0;
385 	}
386 	return 0;
387 }
388 
389 int
390 gss_decrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
391 		    int offset)
392 {
393 	struct decryptor_desc desc;
394 
395 	/* XXXJBF: */
396 	BUG_ON((buf->len - offset) % crypto_blkcipher_blocksize(tfm) != 0);
397 
398 	memset(desc.iv, 0, sizeof(desc.iv));
399 	desc.desc.tfm = tfm;
400 	desc.desc.info = desc.iv;
401 	desc.desc.flags = 0;
402 	desc.fragno = 0;
403 	desc.fraglen = 0;
404 	return process_xdr_buf(buf, offset, buf->len - offset, decryptor, &desc);
405 }
406 
407 EXPORT_SYMBOL(gss_decrypt_xdr_buf);
408