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