ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / sunrpc / auth_gss / gss_mech_switch.c
1 /*
2  *  linux/net/sunrpc/gss_mech_switch.c
3  *
4  *  Copyright (c) 2001 The Regents of the University of Michigan.
5  *  All rights reserved.
6  *
7  *  J. Bruce Fields   <bfields@umich.edu>
8  *
9  *  Redistribution and use in source and binary forms, with or without 
10  *  modification, are permitted provided that the following conditions
11  *  are met:
12  *
13  *  1. Redistributions of source code must retain the above copyright
14  *     notice, this list of conditions and the following disclaimer.
15  *  2. Redistributions in binary form must reproduce the above copyright
16  *     notice, this list of conditions and the following disclaimer in the 
17  *     documentation and/or other materials provided with the distribution.
18  *  3. Neither the name of the University nor the names of its
19  *     contributors may be used to endorse or promote products derived
20  *     from this software without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35
36 #include <linux/types.h>
37 #include <linux/slab.h>
38 #include <linux/socket.h>
39 #include <linux/sunrpc/msg_prot.h>
40 #include <linux/sunrpc/gss_asn1.h>
41 #include <linux/sunrpc/auth_gss.h>
42 #include <linux/sunrpc/gss_err.h>
43 #include <linux/sunrpc/sched.h>
44 #include <linux/sunrpc/gss_api.h>
45 #include <linux/sunrpc/clnt.h>
46
47 #ifdef RPC_DEBUG
48 # define RPCDBG_FACILITY        RPCDBG_AUTH
49 #endif
50
51 static LIST_HEAD(registered_mechs);
52 static spinlock_t registered_mechs_lock = SPIN_LOCK_UNLOCKED;
53
54 /* Reference counting: The reference count includes the reference in the
55  * global registered_mechs list.  That reference will never diseappear
56  * (so the reference count will never go below 1) until after the mech
57  * is removed from the list.  Nothing can be removed from the list without
58  * first getting the registered_mechs_lock, so a gss_api_mech won't diseappear
59  * from underneath us while we hold the registered_mech_lock.  */
60
61 int
62 gss_mech_register(struct xdr_netobj * mech_type, struct gss_api_ops * ops)
63 {
64         struct gss_api_mech *gm;
65
66         if (!(gm = kmalloc(sizeof(*gm), GFP_KERNEL))) {
67                 printk("Failed to allocate memory in gss_mech_register");
68                 return -1;
69         }
70         gm->gm_oid.len = mech_type->len;
71         if (!(gm->gm_oid.data = kmalloc(mech_type->len, GFP_KERNEL))) {
72                 kfree(gm);
73                 printk("Failed to allocate memory in gss_mech_register");
74                 return -1;
75         }
76         memcpy(gm->gm_oid.data, mech_type->data, mech_type->len);
77         /* We're counting the reference in the registered_mechs list: */
78         atomic_set(&gm->gm_count, 1);
79         gm->gm_ops = ops;
80         
81         spin_lock(&registered_mechs_lock);
82         list_add(&gm->gm_list, &registered_mechs);
83         spin_unlock(&registered_mechs_lock);
84         dprintk("RPC: gss_mech_register: registered mechanism with oid:\n");
85         print_hexl((u32 *)mech_type->data, mech_type->len, 0);
86         return 0;
87 }
88
89 /* The following must be called with spinlock held: */
90 int
91 do_gss_mech_unregister(struct gss_api_mech *gm)
92 {
93
94         list_del(&gm->gm_list);
95
96         dprintk("RPC: unregistered mechanism with oid:\n");
97         print_hexl((u32 *)gm->gm_oid.data, gm->gm_oid.len, 0);
98         if (!gss_mech_put(gm)) {
99                 dprintk("RPC: We just unregistered a gss_mechanism which"
100                                 " someone is still using.\n");
101                 return -1;
102         } else {
103                 return 0;
104         }
105 }
106
107 int
108 gss_mech_unregister(struct gss_api_mech *gm)
109 {
110         int status;
111
112         spin_lock(&registered_mechs_lock);
113         status = do_gss_mech_unregister(gm);
114         spin_unlock(&registered_mechs_lock);
115         return status;
116 }
117
118 int
119 gss_mech_unregister_all(void)
120 {
121         struct list_head        *pos;
122         struct gss_api_mech     *gm;
123         int                     status = 0;
124
125         spin_lock(&registered_mechs_lock);
126         while (!list_empty(&registered_mechs)) {
127                 pos = registered_mechs.next;
128                 gm = list_entry(pos, struct gss_api_mech, gm_list);
129                 if (do_gss_mech_unregister(gm))
130                         status = -1;
131         }
132         spin_unlock(&registered_mechs_lock);
133         return status;
134 }
135
136 struct gss_api_mech *
137 gss_mech_get(struct gss_api_mech *gm)
138 {
139         atomic_inc(&gm->gm_count);
140         return gm;
141 }
142
143 struct gss_api_mech *
144 gss_mech_get_by_OID(struct xdr_netobj *mech_type)
145 {
146         struct gss_api_mech     *pos, *gm = NULL;
147
148         dprintk("RPC: gss_mech_get_by_OID searching for mechanism with OID:\n");
149         print_hexl((u32 *)mech_type->data, mech_type->len, 0);
150         spin_lock(&registered_mechs_lock);
151         list_for_each_entry(pos, &registered_mechs, gm_list) {
152                 if ((pos->gm_oid.len == mech_type->len)
153                         && !memcmp(pos->gm_oid.data, mech_type->data,
154                                                         mech_type->len)) {
155                         gm = gss_mech_get(pos);
156                         break;
157                 }
158         }
159         spin_unlock(&registered_mechs_lock);
160         dprintk("RPC: gss_mech_get_by_OID %s it\n", gm ? "found" : "didn't find");
161         return gm;
162 }
163
164 struct gss_api_mech *
165 gss_mech_get_by_name(char *name)
166 {
167         struct gss_api_mech     *pos, *gm = NULL;
168
169         spin_lock(&registered_mechs_lock);
170         list_for_each_entry(pos, &registered_mechs, gm_list) {
171                 if (0 == strcmp(name, pos->gm_ops->name)) {
172                         gm = gss_mech_get(pos);
173                         break;
174                 }
175         }
176         spin_unlock(&registered_mechs_lock);
177         return gm;
178
179 }
180
181 int
182 gss_mech_put(struct gss_api_mech * gm)
183 {
184         if (atomic_dec_and_test(&gm->gm_count)) {
185                 if (gm->gm_oid.len >0)
186                         kfree(gm->gm_oid.data);
187                 kfree(gm);
188                 return 1;
189         } else {
190                 return 0;
191         }
192 }
193
194 /* The mech could probably be determined from the token instead, but it's just
195  * as easy for now to pass it in. */
196 u32
197 gss_import_sec_context(struct xdr_netobj        *input_token,
198                        struct gss_api_mech      *mech,
199                        struct gss_ctx           **ctx_id)
200 {
201         if (!(*ctx_id = kmalloc(sizeof(**ctx_id), GFP_KERNEL)))
202                 return GSS_S_FAILURE;
203         memset(*ctx_id, 0, sizeof(**ctx_id));
204         (*ctx_id)->mech_type = gss_mech_get(mech);
205
206         return mech->gm_ops
207                 ->gss_import_sec_context(input_token, *ctx_id);
208 }
209
210 /* gss_get_mic: compute a mic over message and return mic_token. */
211
212 u32
213 gss_get_mic(struct gss_ctx      *context_handle,
214             u32                 qop,
215             struct xdr_buf      *message,
216             struct xdr_netobj   *mic_token)
217 {
218          return context_handle->mech_type->gm_ops
219                 ->gss_get_mic(context_handle,
220                               qop,
221                               message,
222                               mic_token);
223 }
224
225 /* gss_verify_mic: check whether the provided mic_token verifies message. */
226
227 u32
228 gss_verify_mic(struct gss_ctx           *context_handle,
229                struct xdr_buf           *message,
230                struct xdr_netobj        *mic_token,
231                u32                      *qstate)
232 {
233         return context_handle->mech_type->gm_ops
234                 ->gss_verify_mic(context_handle,
235                                  message,
236                                  mic_token,
237                                  qstate);
238 }
239
240 /* gss_delete_sec_context: free all resources associated with context_handle.
241  * Note this differs from the RFC 2744-specified prototype in that we don't
242  * bother returning an output token, since it would never be used anyway. */
243
244 u32
245 gss_delete_sec_context(struct gss_ctx   **context_handle)
246 {
247         dprintk("gss_delete_sec_context deleting %p\n",*context_handle);
248
249         if (!*context_handle)
250                 return(GSS_S_NO_CONTEXT);
251         if ((*context_handle)->internal_ctx_id != 0)
252                 (*context_handle)->mech_type->gm_ops
253                         ->gss_delete_sec_context((*context_handle)
254                                                         ->internal_ctx_id);
255         if ((*context_handle)->mech_type)
256                 gss_mech_put((*context_handle)->mech_type);
257         kfree(*context_handle);
258         *context_handle=NULL;
259         return GSS_S_COMPLETE;
260 }