ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / 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("krb5_encrypt returns %d\n",ret);
88         return(ret);
89 }
90
91 u32
92 krb5_decrypt(
93      struct crypto_tfm *tfm,
94      void * iv,
95      void * in,
96      void * out,
97      int length)
98 {
99         u32 ret = -EINVAL;
100         struct scatterlist sg[1];
101         u8 local_iv[16] = {0};
102
103         dprintk("RPC: krb5_decrypt: input data:\n");
104         print_hexl((u32 *)in, length, 0);
105
106         if (length % crypto_tfm_alg_blocksize(tfm) != 0)
107                 goto out;
108
109         if (crypto_tfm_alg_ivsize(tfm) > 16) {
110                 dprintk("RPC: gss_k5decrypt: tfm iv size to large %d\n",
111                         crypto_tfm_alg_ivsize(tfm));
112                 goto out;
113         }
114         if (iv)
115                 memcpy(local_iv,iv, crypto_tfm_alg_ivsize(tfm));
116
117         memcpy(out, in, length);
118         sg[0].page = virt_to_page(out);
119         sg[0].offset = offset_in_page(out);
120         sg[0].length = length;
121
122         ret = crypto_cipher_decrypt_iv(tfm, sg, sg, length, local_iv);
123
124         dprintk("RPC: krb5_decrypt: output_data:\n");
125         print_hexl((u32 *)out, length, 0);
126 out:
127         dprintk("gss_k5decrypt returns %d\n",ret);
128         return(ret);
129 }
130
131 void
132 buf_to_sg(struct scatterlist *sg, char *ptr, int len) {
133         sg->page = virt_to_page(ptr);
134         sg->offset = offset_in_page(ptr);
135         sg->length = len;
136 }
137
138 /* checksum the plaintext data and the first 8 bytes of the krb5 token header,
139  * as specified by the rfc: */
140 s32
141 krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body,
142                    struct xdr_netobj *cksum)
143 {
144         char                            *cksumname;
145         struct crypto_tfm               *tfm = NULL; /* XXX add to ctx? */
146         struct scatterlist              sg[1];
147         u32                             code = GSS_S_FAILURE;
148         int                             len, thislen, offset;
149         int                             i;
150
151         switch (cksumtype) {
152                 case CKSUMTYPE_RSA_MD5:
153                         cksumname = "md5";
154                         break;
155                 default:
156                         dprintk("RPC: krb5_make_checksum:"
157                                 " unsupported checksum %d", cksumtype);
158                         goto out;
159         }
160         if (!(tfm = crypto_alloc_tfm(cksumname, 0)))
161                 goto out;
162         cksum->len = crypto_tfm_alg_digestsize(tfm);
163         if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL)
164                 goto out;
165
166         crypto_digest_init(tfm);
167         buf_to_sg(sg, header, 8);
168         crypto_digest_update(tfm, sg, 1);
169         if (body->head[0].iov_len) {
170                 buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len);
171                 crypto_digest_update(tfm, sg, 1);
172         }
173
174         len = body->page_len;
175         if (len != 0) {
176                 offset = body->page_base & (PAGE_CACHE_SIZE - 1);
177                 i = body->page_base >> PAGE_CACHE_SHIFT;
178                 thislen = PAGE_CACHE_SIZE - offset;
179                 do {
180                         if (thislen > len)
181                                 thislen = len;
182                         sg->page = body->pages[i];
183                         sg->offset = offset;
184                         sg->length = thislen;
185                         kmap(sg->page); /* XXX kmap_atomic? */
186                         crypto_digest_update(tfm, sg, 1);
187                         kunmap(sg->page);
188                         len -= thislen;
189                         i++;
190                         offset = 0;
191                         thislen = PAGE_CACHE_SIZE;
192                 } while(len != 0);
193         }
194         if (body->tail[0].iov_len) {
195                 buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len);
196                 crypto_digest_update(tfm, sg, 1);
197         }
198         crypto_digest_final(tfm, cksum->data);
199         code = 0;
200 out:
201         if (tfm)
202                 crypto_free_tfm(tfm);
203         return code;
204 }