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_mech.c
1 /*
2  *  linux/net/sunrpc/gss_krb5_mech.c
3  *
4  *  Copyright (c) 2001 The Regents of the University of Michigan.
5  *  All rights reserved.
6  *
7  *  Andy Adamson <andros@umich.edu>
8  *  J. Bruce Fields <bfields@umich.edu>
9  *
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions
12  *  are met:
13  *
14  *  1. Redistributions of source code must retain the above copyright
15  *     notice, this list of conditions and the following disclaimer.
16  *  2. Redistributions in binary form must reproduce the above copyright
17  *     notice, this list of conditions and the following disclaimer in the
18  *     documentation and/or other materials provided with the distribution.
19  *  3. Neither the name of the University nor the names of its
20  *     contributors may be used to endorse or promote products derived
21  *     from this software without specific prior written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36
37 #include <linux/module.h>
38 #include <linux/init.h>
39 #include <linux/types.h>
40 #include <linux/slab.h>
41 #include <linux/sunrpc/auth.h>
42 #include <linux/in.h>
43 #include <linux/sunrpc/svcauth_gss.h>
44 #include <linux/sunrpc/gss_krb5.h>
45 #include <linux/sunrpc/xdr.h>
46 #include <linux/crypto.h>
47
48 #ifdef RPC_DEBUG
49 # define RPCDBG_FACILITY        RPCDBG_AUTH
50 #endif
51
52 struct xdr_netobj gss_mech_krb5_oid =
53    {9, "\052\206\110\206\367\022\001\002\002"};
54
55 static inline int
56 get_bytes(char **ptr, const char *end, void *res, int len)
57 {
58         char *p, *q;
59         p = *ptr;
60         q = p + len;
61         if (q > end || q < p)
62                 return -1;
63         memcpy(res, p, len);
64         *ptr = q;
65         return 0;
66 }
67
68 static inline int
69 get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
70 {
71         char *p, *q;
72         p = *ptr;
73         if (get_bytes(&p, end, &res->len, sizeof(res->len)))
74                 return -1;
75         q = p + res->len;
76         if (q > end || q < p)
77                 return -1;
78         if (!(res->data = kmalloc(res->len, GFP_KERNEL)))
79                 return -1;
80         memcpy(res->data, p, res->len);
81         *ptr = q;
82         return 0;
83 }
84
85 static inline int
86 get_key(char **p, char *end, struct crypto_tfm **res)
87 {
88         struct xdr_netobj       key;
89         int                     alg, alg_mode;
90         char                    *alg_name;
91
92         if (get_bytes(p, end, &alg, sizeof(alg)))
93                 goto out_err;
94         if ((get_netobj(p, end, &key)))
95                 goto out_err;
96
97         switch (alg) {
98                 case ENCTYPE_DES_CBC_RAW:
99                         alg_name = "des";
100                         alg_mode = CRYPTO_TFM_MODE_CBC;
101                         break;
102                 default:
103                         dprintk("RPC: get_key: unsupported algorithm %d\n", alg);
104                         goto out_err_free_key;
105         }
106         if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
107                 goto out_err_free_key;
108         if (crypto_cipher_setkey(*res, key.data, key.len))
109                 goto out_err_free_tfm;
110
111         kfree(key.data);
112         return 0;
113
114 out_err_free_tfm:
115         crypto_free_tfm(*res);
116 out_err_free_key:
117         kfree(key.data);
118 out_err:
119         return -1;
120 }
121
122 static u32
123 gss_import_sec_context_kerberos(struct xdr_netobj *inbuf,
124                                 struct gss_ctx *ctx_id)
125 {
126         char    *p = inbuf->data;
127         char    *end = inbuf->data + inbuf->len;
128         struct  krb5_ctx *ctx;
129
130         if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)))
131                 goto out_err;
132         memset(ctx, 0, sizeof(*ctx));
133
134         if (get_bytes(&p, end, &ctx->initiate, sizeof(ctx->initiate)))
135                 goto out_err_free_ctx;
136         if (get_bytes(&p, end, &ctx->seed_init, sizeof(ctx->seed_init)))
137                 goto out_err_free_ctx;
138         if (get_bytes(&p, end, ctx->seed, sizeof(ctx->seed)))
139                 goto out_err_free_ctx;
140         if (get_bytes(&p, end, &ctx->signalg, sizeof(ctx->signalg)))
141                 goto out_err_free_ctx;
142         if (get_bytes(&p, end, &ctx->sealalg, sizeof(ctx->sealalg)))
143                 goto out_err_free_ctx;
144         if (get_bytes(&p, end, &ctx->endtime, sizeof(ctx->endtime)))
145                 goto out_err_free_ctx;
146         if (get_bytes(&p, end, &ctx->seq_send, sizeof(ctx->seq_send)))
147                 goto out_err_free_ctx;
148         if (get_netobj(&p, end, &ctx->mech_used))
149                 goto out_err_free_ctx;
150         if (get_key(&p, end, &ctx->enc))
151                 goto out_err_free_mech;
152         if (get_key(&p, end, &ctx->seq))
153                 goto out_err_free_key1;
154         if (p != end)
155                 goto out_err_free_key2;
156
157         ctx_id->internal_ctx_id = ctx;
158         dprintk("Succesfully imported new context.\n");
159         return 0;
160
161 out_err_free_key2:
162         crypto_free_tfm(ctx->seq);
163 out_err_free_key1:
164         crypto_free_tfm(ctx->enc);
165 out_err_free_mech:
166         kfree(ctx->mech_used.data);
167 out_err_free_ctx:
168         kfree(ctx);
169 out_err:
170         return GSS_S_FAILURE;
171 }
172
173 static void
174 gss_delete_sec_context_kerberos(void *internal_ctx) {
175         struct krb5_ctx *kctx = internal_ctx;
176
177         if (kctx->seq)
178                 crypto_free_tfm(kctx->seq);
179         if (kctx->enc)
180                 crypto_free_tfm(kctx->enc);
181         if (kctx->mech_used.data)
182                 kfree(kctx->mech_used.data);
183         kfree(kctx);
184 }
185
186 static u32
187 gss_verify_mic_kerberos(struct gss_ctx          *ctx,
188                         struct xdr_buf          *message,
189                         struct xdr_netobj       *mic_token,
190                         u32                     *qstate) {
191         u32 maj_stat = 0;
192         int qop_state;
193         struct krb5_ctx *kctx = ctx->internal_ctx_id;
194
195         maj_stat = krb5_read_token(kctx, mic_token, message, &qop_state,
196                                    KG_TOK_MIC_MSG);
197         if (!maj_stat && qop_state)
198             *qstate = qop_state;
199
200         dprintk("RPC: gss_verify_mic_kerberos returning %d\n", maj_stat);
201         return maj_stat;
202 }
203
204 static u32
205 gss_get_mic_kerberos(struct gss_ctx     *ctx,
206                      u32                qop,
207                      struct xdr_buf     *message,
208                      struct xdr_netobj  *mic_token) {
209         u32 err = 0;
210         struct krb5_ctx *kctx = ctx->internal_ctx_id;
211
212         err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG);
213
214         dprintk("RPC: gss_get_mic_kerberos returning %d\n",err);
215
216         return err;
217 }
218
219 static struct gss_api_ops gss_kerberos_ops = {
220         .name                   = "krb5",
221         .gss_import_sec_context = gss_import_sec_context_kerberos,
222         .gss_get_mic            = gss_get_mic_kerberos,
223         .gss_verify_mic         = gss_verify_mic_kerberos,
224         .gss_delete_sec_context = gss_delete_sec_context_kerberos,
225 };
226
227 /* XXX error checking? reference counting? */
228 static int __init init_kerberos_module(void)
229 {
230         struct gss_api_mech *gm;
231
232         if (gss_mech_register(&gss_mech_krb5_oid, &gss_kerberos_ops))
233                 printk("Failed to register kerberos gss mechanism!\n");
234         gm = gss_mech_get_by_OID(&gss_mech_krb5_oid);
235         gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE);
236         gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY);
237         if (svcauth_gss_register_pseudoflavor(RPC_AUTH_GSS_KRB5, "krb5"))
238                 printk("Failed to register %s with server!\n", "krb5");
239         if (svcauth_gss_register_pseudoflavor(RPC_AUTH_GSS_KRB5I, "krb5i"))
240                 printk("Failed to register %s with server!\n", "krb5i");
241         gss_mech_put(gm);
242         return 0;
243 }
244
245 static void __exit cleanup_kerberos_module(void)
246 {
247         gss_unregister_triple(RPC_AUTH_GSS_KRB5I);
248         gss_unregister_triple(RPC_AUTH_GSS_KRB5);
249 }
250
251 MODULE_LICENSE("GPL");
252 module_init(init_kerberos_module);
253 module_exit(cleanup_kerberos_module);