832ce901bf6813a25c7cc2f620752298b7c84630
[safe/jmp/linux-2.6] / net / sunrpc / auth_gss / gss_krb5_keys.c
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 /*
32  * Copyright (C) 1998 by the FundsXpress, INC.
33  *
34  * All rights reserved.
35  *
36  * Export of this software from the United States of America may require
37  * a specific license from the United States Government.  It is the
38  * responsibility of any person or organization contemplating export to
39  * obtain such a license before exporting.
40  *
41  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42  * distribute this software and its documentation for any purpose and
43  * without fee is hereby granted, provided that the above copyright
44  * notice appear in all copies and that both that copyright notice and
45  * this permission notice appear in supporting documentation, and that
46  * the name of FundsXpress. not be used in advertising or publicity pertaining
47  * to distribution of the software without specific, written prior
48  * permission.  FundsXpress makes no representations about the suitability of
49  * this software for any purpose.  It is provided "as is" without express
50  * or implied warranty.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
53  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
54  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
55  */
56
57 #include <linux/err.h>
58 #include <linux/types.h>
59 #include <linux/crypto.h>
60 #include <linux/sunrpc/gss_krb5.h>
61 #include <linux/sunrpc/xdr.h>
62
63 #ifdef RPC_DEBUG
64 # define RPCDBG_FACILITY        RPCDBG_AUTH
65 #endif
66
67 /*
68  * This is the n-fold function as described in rfc3961, sec 5.1
69  * Taken from MIT Kerberos and modified.
70  */
71
72 static void krb5_nfold(u32 inbits, const u8 *in,
73                        u32 outbits, u8 *out)
74 {
75         int a, b, c, lcm;
76         int byte, i, msbit;
77
78         /* the code below is more readable if I make these bytes
79            instead of bits */
80
81         inbits >>= 3;
82         outbits >>= 3;
83
84         /* first compute lcm(n,k) */
85
86         a = outbits;
87         b = inbits;
88
89         while (b != 0) {
90                 c = b;
91                 b = a%b;
92                 a = c;
93         }
94
95         lcm = outbits*inbits/a;
96
97         /* now do the real work */
98
99         memset(out, 0, outbits);
100         byte = 0;
101
102         /* this will end up cycling through k lcm(k,n)/k times, which
103            is correct */
104         for (i = lcm-1; i >= 0; i--) {
105                 /* compute the msbit in k which gets added into this byte */
106                 msbit = (
107                         /* first, start with the msbit in the first,
108                          * unrotated byte */
109                          ((inbits << 3) - 1)
110                          /* then, for each byte, shift to the right
111                           * for each repetition */
112                          + (((inbits << 3) + 13) * (i/inbits))
113                          /* last, pick out the correct byte within
114                           * that shifted repetition */
115                          + ((inbits - (i % inbits)) << 3)
116                          ) % (inbits << 3);
117
118                 /* pull out the byte value itself */
119                 byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)|
120                                   (in[((inbits) - (msbit >> 3)) % inbits]))
121                                  >> ((msbit & 7) + 1)) & 0xff;
122
123                 /* do the addition */
124                 byte += out[i % outbits];
125                 out[i % outbits] = byte & 0xff;
126
127                 /* keep around the carry bit, if any */
128                 byte >>= 8;
129
130         }
131
132         /* if there's a carry bit left over, add it back in */
133         if (byte) {
134                 for (i = outbits - 1; i >= 0; i--) {
135                         /* do the addition */
136                         byte += out[i];
137                         out[i] = byte & 0xff;
138
139                         /* keep around the carry bit, if any */
140                         byte >>= 8;
141                 }
142         }
143 }
144
145 /*
146  * This is the DK (derive_key) function as described in rfc3961, sec 5.1
147  * Taken from MIT Kerberos and modified.
148  */
149
150 u32 krb5_derive_key(struct gss_krb5_enctype *gk5e,
151                     const struct xdr_netobj *inkey,
152                     struct xdr_netobj *outkey,
153                     const struct xdr_netobj *in_constant)
154 {
155         size_t blocksize, keybytes, keylength, n;
156         unsigned char *inblockdata, *outblockdata, *rawkey;
157         struct xdr_netobj inblock, outblock;
158         struct crypto_blkcipher *cipher;
159         u32 ret = EINVAL;
160
161         blocksize = gk5e->blocksize;
162         keybytes = gk5e->keybytes;
163         keylength = gk5e->keylength;
164
165         if ((inkey->len != keylength) || (outkey->len != keylength))
166                 goto err_return;
167
168         cipher = crypto_alloc_blkcipher(gk5e->encrypt_name, 0,
169                                         CRYPTO_ALG_ASYNC);
170         if (IS_ERR(cipher))
171                 goto err_return;
172         if (crypto_blkcipher_setkey(cipher, inkey->data, inkey->len))
173                 goto err_return;
174
175         /* allocate and set up buffers */
176
177         ret = ENOMEM;
178         inblockdata = kmalloc(blocksize, GFP_KERNEL);
179         if (inblockdata == NULL)
180                 goto err_free_cipher;
181
182         outblockdata = kmalloc(blocksize, GFP_KERNEL);
183         if (outblockdata == NULL)
184                 goto err_free_in;
185
186         rawkey = kmalloc(keybytes, GFP_KERNEL);
187         if (rawkey == NULL)
188                 goto err_free_out;
189
190         inblock.data = (char *) inblockdata;
191         inblock.len = blocksize;
192
193         outblock.data = (char *) outblockdata;
194         outblock.len = blocksize;
195
196         /* initialize the input block */
197
198         if (in_constant->len == inblock.len) {
199                 memcpy(inblock.data, in_constant->data, inblock.len);
200         } else {
201                 krb5_nfold(in_constant->len * 8, in_constant->data,
202                            inblock.len * 8, inblock.data);
203         }
204
205         /* loop encrypting the blocks until enough key bytes are generated */
206
207         n = 0;
208         while (n < keybytes) {
209                 (*(gk5e->encrypt))(cipher, NULL, inblock.data,
210                                    outblock.data, inblock.len);
211
212                 if ((keybytes - n) <= outblock.len) {
213                         memcpy(rawkey + n, outblock.data, (keybytes - n));
214                         break;
215                 }
216
217                 memcpy(rawkey + n, outblock.data, outblock.len);
218                 memcpy(inblock.data, outblock.data, outblock.len);
219                 n += outblock.len;
220         }
221
222         /* postprocess the key */
223
224         inblock.data = (char *) rawkey;
225         inblock.len = keybytes;
226
227         BUG_ON(gk5e->mk_key == NULL);
228         ret = (*(gk5e->mk_key))(gk5e, &inblock, outkey);
229         if (ret) {
230                 dprintk("%s: got %d from mk_key function for '%s'\n",
231                         __func__, ret, gk5e->encrypt_name);
232                 goto err_free_raw;
233         }
234
235         /* clean memory, free resources and exit */
236
237         ret = 0;
238
239 err_free_raw:
240         memset(rawkey, 0, keybytes);
241         kfree(rawkey);
242 err_free_out:
243         memset(outblockdata, 0, blocksize);
244         kfree(outblockdata);
245 err_free_in:
246         memset(inblockdata, 0, blocksize);
247         kfree(inblockdata);
248 err_free_cipher:
249         crypto_free_blkcipher(cipher);
250 err_return:
251         return ret;
252 }