diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/sunrpc/auth_gss |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/sunrpc/auth_gss')
-rw-r--r-- | net/sunrpc/auth_gss/Makefile | 18 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 1152 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_generic_token.c | 235 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_crypto.c | 209 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_mech.c | 275 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_seal.c | 176 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_seqnum.c | 88 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_unseal.c | 202 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_mech_switch.c | 301 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_spkm3_mech.c | 300 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_spkm3_seal.c | 132 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_spkm3_token.c | 266 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_spkm3_unseal.c | 128 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/svcauth_gss.c | 1080 |
14 files changed, 4562 insertions, 0 deletions
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile new file mode 100644 index 000000000000..fe1b874084bc --- /dev/null +++ b/net/sunrpc/auth_gss/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | # | ||
2 | # Makefile for Linux kernel rpcsec_gss implementation | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o | ||
6 | |||
7 | auth_rpcgss-objs := auth_gss.o gss_generic_token.o \ | ||
8 | gss_mech_switch.o svcauth_gss.o gss_krb5_crypto.o | ||
9 | |||
10 | obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o | ||
11 | |||
12 | rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \ | ||
13 | gss_krb5_seqnum.o | ||
14 | |||
15 | obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o | ||
16 | |||
17 | rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \ | ||
18 | gss_spkm3_token.o | ||
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c new file mode 100644 index 000000000000..a33b627cbef4 --- /dev/null +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -0,0 +1,1152 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/auth_gss.c | ||
3 | * | ||
4 | * RPCSEC_GSS client authentication. | ||
5 | * | ||
6 | * Copyright (c) 2000 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Dug Song <dugsong@monkey.org> | ||
10 | * Andy Adamson <andros@umich.edu> | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or without | ||
13 | * modification, are permitted provided that the following conditions | ||
14 | * are met: | ||
15 | * | ||
16 | * 1. Redistributions of source code must retain the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer. | ||
18 | * 2. Redistributions in binary form must reproduce the above copyright | ||
19 | * notice, this list of conditions and the following disclaimer in the | ||
20 | * documentation and/or other materials provided with the distribution. | ||
21 | * 3. Neither the name of the University nor the names of its | ||
22 | * contributors may be used to endorse or promote products derived | ||
23 | * from this software without specific prior written permission. | ||
24 | * | ||
25 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
26 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
27 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
28 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
32 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
34 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
36 | * | ||
37 | * $Id$ | ||
38 | */ | ||
39 | |||
40 | |||
41 | #include <linux/module.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/types.h> | ||
44 | #include <linux/slab.h> | ||
45 | #include <linux/socket.h> | ||
46 | #include <linux/in.h> | ||
47 | #include <linux/sched.h> | ||
48 | #include <linux/sunrpc/clnt.h> | ||
49 | #include <linux/sunrpc/auth.h> | ||
50 | #include <linux/sunrpc/auth_gss.h> | ||
51 | #include <linux/sunrpc/svcauth_gss.h> | ||
52 | #include <linux/sunrpc/gss_err.h> | ||
53 | #include <linux/workqueue.h> | ||
54 | #include <linux/sunrpc/rpc_pipe_fs.h> | ||
55 | #include <linux/sunrpc/gss_api.h> | ||
56 | #include <asm/uaccess.h> | ||
57 | |||
58 | static struct rpc_authops authgss_ops; | ||
59 | |||
60 | static struct rpc_credops gss_credops; | ||
61 | |||
62 | #ifdef RPC_DEBUG | ||
63 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
64 | #endif | ||
65 | |||
66 | #define NFS_NGROUPS 16 | ||
67 | |||
68 | #define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */ | ||
69 | #define GSS_CRED_SLACK 1024 /* XXX: unused */ | ||
70 | /* length of a krb5 verifier (48), plus data added before arguments when | ||
71 | * using integrity (two 4-byte integers): */ | ||
72 | #define GSS_VERF_SLACK 56 | ||
73 | |||
74 | /* XXX this define must match the gssd define | ||
75 | * as it is passed to gssd to signal the use of | ||
76 | * machine creds should be part of the shared rpc interface */ | ||
77 | |||
78 | #define CA_RUN_AS_MACHINE 0x00000200 | ||
79 | |||
80 | /* dump the buffer in `emacs-hexl' style */ | ||
81 | #define isprint(c) ((c > 0x1f) && (c < 0x7f)) | ||
82 | |||
83 | static DEFINE_RWLOCK(gss_ctx_lock); | ||
84 | |||
85 | struct gss_auth { | ||
86 | struct rpc_auth rpc_auth; | ||
87 | struct gss_api_mech *mech; | ||
88 | enum rpc_gss_svc service; | ||
89 | struct list_head upcalls; | ||
90 | struct rpc_clnt *client; | ||
91 | struct dentry *dentry; | ||
92 | char path[48]; | ||
93 | spinlock_t lock; | ||
94 | }; | ||
95 | |||
96 | static void gss_destroy_ctx(struct gss_cl_ctx *); | ||
97 | static struct rpc_pipe_ops gss_upcall_ops; | ||
98 | |||
99 | void | ||
100 | print_hexl(u32 *p, u_int length, u_int offset) | ||
101 | { | ||
102 | u_int i, j, jm; | ||
103 | u8 c, *cp; | ||
104 | |||
105 | dprintk("RPC: print_hexl: length %d\n",length); | ||
106 | dprintk("\n"); | ||
107 | cp = (u8 *) p; | ||
108 | |||
109 | for (i = 0; i < length; i += 0x10) { | ||
110 | dprintk(" %04x: ", (u_int)(i + offset)); | ||
111 | jm = length - i; | ||
112 | jm = jm > 16 ? 16 : jm; | ||
113 | |||
114 | for (j = 0; j < jm; j++) { | ||
115 | if ((j % 2) == 1) | ||
116 | dprintk("%02x ", (u_int)cp[i+j]); | ||
117 | else | ||
118 | dprintk("%02x", (u_int)cp[i+j]); | ||
119 | } | ||
120 | for (; j < 16; j++) { | ||
121 | if ((j % 2) == 1) | ||
122 | dprintk(" "); | ||
123 | else | ||
124 | dprintk(" "); | ||
125 | } | ||
126 | dprintk(" "); | ||
127 | |||
128 | for (j = 0; j < jm; j++) { | ||
129 | c = cp[i+j]; | ||
130 | c = isprint(c) ? c : '.'; | ||
131 | dprintk("%c", c); | ||
132 | } | ||
133 | dprintk("\n"); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | EXPORT_SYMBOL(print_hexl); | ||
138 | |||
139 | static inline struct gss_cl_ctx * | ||
140 | gss_get_ctx(struct gss_cl_ctx *ctx) | ||
141 | { | ||
142 | atomic_inc(&ctx->count); | ||
143 | return ctx; | ||
144 | } | ||
145 | |||
146 | static inline void | ||
147 | gss_put_ctx(struct gss_cl_ctx *ctx) | ||
148 | { | ||
149 | if (atomic_dec_and_test(&ctx->count)) | ||
150 | gss_destroy_ctx(ctx); | ||
151 | } | ||
152 | |||
153 | static void | ||
154 | gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) | ||
155 | { | ||
156 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); | ||
157 | struct gss_cl_ctx *old; | ||
158 | write_lock(&gss_ctx_lock); | ||
159 | old = gss_cred->gc_ctx; | ||
160 | gss_cred->gc_ctx = ctx; | ||
161 | cred->cr_flags |= RPCAUTH_CRED_UPTODATE; | ||
162 | write_unlock(&gss_ctx_lock); | ||
163 | if (old) | ||
164 | gss_put_ctx(old); | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | gss_cred_is_uptodate_ctx(struct rpc_cred *cred) | ||
169 | { | ||
170 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); | ||
171 | int res = 0; | ||
172 | |||
173 | read_lock(&gss_ctx_lock); | ||
174 | if ((cred->cr_flags & RPCAUTH_CRED_UPTODATE) && gss_cred->gc_ctx) | ||
175 | res = 1; | ||
176 | read_unlock(&gss_ctx_lock); | ||
177 | return res; | ||
178 | } | ||
179 | |||
180 | static const void * | ||
181 | simple_get_bytes(const void *p, const void *end, void *res, size_t len) | ||
182 | { | ||
183 | const void *q = (const void *)((const char *)p + len); | ||
184 | if (unlikely(q > end || q < p)) | ||
185 | return ERR_PTR(-EFAULT); | ||
186 | memcpy(res, p, len); | ||
187 | return q; | ||
188 | } | ||
189 | |||
190 | static inline const void * | ||
191 | simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest) | ||
192 | { | ||
193 | const void *q; | ||
194 | unsigned int len; | ||
195 | |||
196 | p = simple_get_bytes(p, end, &len, sizeof(len)); | ||
197 | if (IS_ERR(p)) | ||
198 | return p; | ||
199 | q = (const void *)((const char *)p + len); | ||
200 | if (unlikely(q > end || q < p)) | ||
201 | return ERR_PTR(-EFAULT); | ||
202 | dest->data = kmalloc(len, GFP_KERNEL); | ||
203 | if (unlikely(dest->data == NULL)) | ||
204 | return ERR_PTR(-ENOMEM); | ||
205 | dest->len = len; | ||
206 | memcpy(dest->data, p, len); | ||
207 | return q; | ||
208 | } | ||
209 | |||
210 | static struct gss_cl_ctx * | ||
211 | gss_cred_get_ctx(struct rpc_cred *cred) | ||
212 | { | ||
213 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); | ||
214 | struct gss_cl_ctx *ctx = NULL; | ||
215 | |||
216 | read_lock(&gss_ctx_lock); | ||
217 | if (gss_cred->gc_ctx) | ||
218 | ctx = gss_get_ctx(gss_cred->gc_ctx); | ||
219 | read_unlock(&gss_ctx_lock); | ||
220 | return ctx; | ||
221 | } | ||
222 | |||
223 | static struct gss_cl_ctx * | ||
224 | gss_alloc_context(void) | ||
225 | { | ||
226 | struct gss_cl_ctx *ctx; | ||
227 | |||
228 | ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); | ||
229 | if (ctx != NULL) { | ||
230 | memset(ctx, 0, sizeof(*ctx)); | ||
231 | ctx->gc_proc = RPC_GSS_PROC_DATA; | ||
232 | ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ | ||
233 | spin_lock_init(&ctx->gc_seq_lock); | ||
234 | atomic_set(&ctx->count,1); | ||
235 | } | ||
236 | return ctx; | ||
237 | } | ||
238 | |||
239 | #define GSSD_MIN_TIMEOUT (60 * 60) | ||
240 | static const void * | ||
241 | gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm) | ||
242 | { | ||
243 | const void *q; | ||
244 | unsigned int seclen; | ||
245 | unsigned int timeout; | ||
246 | u32 window_size; | ||
247 | int ret; | ||
248 | |||
249 | /* First unsigned int gives the lifetime (in seconds) of the cred */ | ||
250 | p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); | ||
251 | if (IS_ERR(p)) | ||
252 | goto err; | ||
253 | if (timeout == 0) | ||
254 | timeout = GSSD_MIN_TIMEOUT; | ||
255 | ctx->gc_expiry = jiffies + (unsigned long)timeout * HZ * 3 / 4; | ||
256 | /* Sequence number window. Determines the maximum number of simultaneous requests */ | ||
257 | p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); | ||
258 | if (IS_ERR(p)) | ||
259 | goto err; | ||
260 | ctx->gc_win = window_size; | ||
261 | /* gssd signals an error by passing ctx->gc_win = 0: */ | ||
262 | if (ctx->gc_win == 0) { | ||
263 | /* in which case, p points to an error code which we ignore */ | ||
264 | p = ERR_PTR(-EACCES); | ||
265 | goto err; | ||
266 | } | ||
267 | /* copy the opaque wire context */ | ||
268 | p = simple_get_netobj(p, end, &ctx->gc_wire_ctx); | ||
269 | if (IS_ERR(p)) | ||
270 | goto err; | ||
271 | /* import the opaque security context */ | ||
272 | p = simple_get_bytes(p, end, &seclen, sizeof(seclen)); | ||
273 | if (IS_ERR(p)) | ||
274 | goto err; | ||
275 | q = (const void *)((const char *)p + seclen); | ||
276 | if (unlikely(q > end || q < p)) { | ||
277 | p = ERR_PTR(-EFAULT); | ||
278 | goto err; | ||
279 | } | ||
280 | ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx); | ||
281 | if (ret < 0) { | ||
282 | p = ERR_PTR(ret); | ||
283 | goto err; | ||
284 | } | ||
285 | return q; | ||
286 | err: | ||
287 | dprintk("RPC: gss_fill_context returning %ld\n", -PTR_ERR(p)); | ||
288 | return p; | ||
289 | } | ||
290 | |||
291 | |||
292 | struct gss_upcall_msg { | ||
293 | atomic_t count; | ||
294 | uid_t uid; | ||
295 | struct rpc_pipe_msg msg; | ||
296 | struct list_head list; | ||
297 | struct gss_auth *auth; | ||
298 | struct rpc_wait_queue rpc_waitqueue; | ||
299 | wait_queue_head_t waitqueue; | ||
300 | struct gss_cl_ctx *ctx; | ||
301 | }; | ||
302 | |||
303 | static void | ||
304 | gss_release_msg(struct gss_upcall_msg *gss_msg) | ||
305 | { | ||
306 | if (!atomic_dec_and_test(&gss_msg->count)) | ||
307 | return; | ||
308 | BUG_ON(!list_empty(&gss_msg->list)); | ||
309 | if (gss_msg->ctx != NULL) | ||
310 | gss_put_ctx(gss_msg->ctx); | ||
311 | kfree(gss_msg); | ||
312 | } | ||
313 | |||
314 | static struct gss_upcall_msg * | ||
315 | __gss_find_upcall(struct gss_auth *gss_auth, uid_t uid) | ||
316 | { | ||
317 | struct gss_upcall_msg *pos; | ||
318 | list_for_each_entry(pos, &gss_auth->upcalls, list) { | ||
319 | if (pos->uid != uid) | ||
320 | continue; | ||
321 | atomic_inc(&pos->count); | ||
322 | dprintk("RPC: gss_find_upcall found msg %p\n", pos); | ||
323 | return pos; | ||
324 | } | ||
325 | dprintk("RPC: gss_find_upcall found nothing\n"); | ||
326 | return NULL; | ||
327 | } | ||
328 | |||
329 | /* Try to add a upcall to the pipefs queue. | ||
330 | * If an upcall owned by our uid already exists, then we return a reference | ||
331 | * to that upcall instead of adding the new upcall. | ||
332 | */ | ||
333 | static inline struct gss_upcall_msg * | ||
334 | gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) | ||
335 | { | ||
336 | struct gss_upcall_msg *old; | ||
337 | |||
338 | spin_lock(&gss_auth->lock); | ||
339 | old = __gss_find_upcall(gss_auth, gss_msg->uid); | ||
340 | if (old == NULL) { | ||
341 | atomic_inc(&gss_msg->count); | ||
342 | list_add(&gss_msg->list, &gss_auth->upcalls); | ||
343 | } else | ||
344 | gss_msg = old; | ||
345 | spin_unlock(&gss_auth->lock); | ||
346 | return gss_msg; | ||
347 | } | ||
348 | |||
349 | static void | ||
350 | __gss_unhash_msg(struct gss_upcall_msg *gss_msg) | ||
351 | { | ||
352 | if (list_empty(&gss_msg->list)) | ||
353 | return; | ||
354 | list_del_init(&gss_msg->list); | ||
355 | rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); | ||
356 | wake_up_all(&gss_msg->waitqueue); | ||
357 | atomic_dec(&gss_msg->count); | ||
358 | } | ||
359 | |||
360 | static void | ||
361 | gss_unhash_msg(struct gss_upcall_msg *gss_msg) | ||
362 | { | ||
363 | struct gss_auth *gss_auth = gss_msg->auth; | ||
364 | |||
365 | spin_lock(&gss_auth->lock); | ||
366 | __gss_unhash_msg(gss_msg); | ||
367 | spin_unlock(&gss_auth->lock); | ||
368 | } | ||
369 | |||
370 | static void | ||
371 | gss_upcall_callback(struct rpc_task *task) | ||
372 | { | ||
373 | struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, | ||
374 | struct gss_cred, gc_base); | ||
375 | struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; | ||
376 | |||
377 | BUG_ON(gss_msg == NULL); | ||
378 | if (gss_msg->ctx) | ||
379 | gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx)); | ||
380 | else | ||
381 | task->tk_status = gss_msg->msg.errno; | ||
382 | spin_lock(&gss_msg->auth->lock); | ||
383 | gss_cred->gc_upcall = NULL; | ||
384 | rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); | ||
385 | spin_unlock(&gss_msg->auth->lock); | ||
386 | gss_release_msg(gss_msg); | ||
387 | } | ||
388 | |||
389 | static inline struct gss_upcall_msg * | ||
390 | gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid) | ||
391 | { | ||
392 | struct gss_upcall_msg *gss_msg; | ||
393 | |||
394 | gss_msg = kmalloc(sizeof(*gss_msg), GFP_KERNEL); | ||
395 | if (gss_msg != NULL) { | ||
396 | memset(gss_msg, 0, sizeof(*gss_msg)); | ||
397 | INIT_LIST_HEAD(&gss_msg->list); | ||
398 | rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); | ||
399 | init_waitqueue_head(&gss_msg->waitqueue); | ||
400 | atomic_set(&gss_msg->count, 1); | ||
401 | gss_msg->msg.data = &gss_msg->uid; | ||
402 | gss_msg->msg.len = sizeof(gss_msg->uid); | ||
403 | gss_msg->uid = uid; | ||
404 | gss_msg->auth = gss_auth; | ||
405 | } | ||
406 | return gss_msg; | ||
407 | } | ||
408 | |||
409 | static struct gss_upcall_msg * | ||
410 | gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred) | ||
411 | { | ||
412 | struct gss_upcall_msg *gss_new, *gss_msg; | ||
413 | |||
414 | gss_new = gss_alloc_msg(gss_auth, cred->cr_uid); | ||
415 | if (gss_new == NULL) | ||
416 | return ERR_PTR(-ENOMEM); | ||
417 | gss_msg = gss_add_msg(gss_auth, gss_new); | ||
418 | if (gss_msg == gss_new) { | ||
419 | int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg); | ||
420 | if (res) { | ||
421 | gss_unhash_msg(gss_new); | ||
422 | gss_msg = ERR_PTR(res); | ||
423 | } | ||
424 | } else | ||
425 | gss_release_msg(gss_new); | ||
426 | return gss_msg; | ||
427 | } | ||
428 | |||
429 | static inline int | ||
430 | gss_refresh_upcall(struct rpc_task *task) | ||
431 | { | ||
432 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | ||
433 | struct gss_auth *gss_auth = container_of(task->tk_client->cl_auth, | ||
434 | struct gss_auth, rpc_auth); | ||
435 | struct gss_cred *gss_cred = container_of(cred, | ||
436 | struct gss_cred, gc_base); | ||
437 | struct gss_upcall_msg *gss_msg; | ||
438 | int err = 0; | ||
439 | |||
440 | dprintk("RPC: %4u gss_refresh_upcall for uid %u\n", task->tk_pid, cred->cr_uid); | ||
441 | gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); | ||
442 | if (IS_ERR(gss_msg)) { | ||
443 | err = PTR_ERR(gss_msg); | ||
444 | goto out; | ||
445 | } | ||
446 | spin_lock(&gss_auth->lock); | ||
447 | if (gss_cred->gc_upcall != NULL) | ||
448 | rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL, NULL); | ||
449 | else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { | ||
450 | task->tk_timeout = 0; | ||
451 | gss_cred->gc_upcall = gss_msg; | ||
452 | /* gss_upcall_callback will release the reference to gss_upcall_msg */ | ||
453 | atomic_inc(&gss_msg->count); | ||
454 | rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback, NULL); | ||
455 | } else | ||
456 | err = gss_msg->msg.errno; | ||
457 | spin_unlock(&gss_auth->lock); | ||
458 | gss_release_msg(gss_msg); | ||
459 | out: | ||
460 | dprintk("RPC: %4u gss_refresh_upcall for uid %u result %d\n", task->tk_pid, | ||
461 | cred->cr_uid, err); | ||
462 | return err; | ||
463 | } | ||
464 | |||
465 | static inline int | ||
466 | gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) | ||
467 | { | ||
468 | struct rpc_cred *cred = &gss_cred->gc_base; | ||
469 | struct gss_upcall_msg *gss_msg; | ||
470 | DEFINE_WAIT(wait); | ||
471 | int err = 0; | ||
472 | |||
473 | dprintk("RPC: gss_upcall for uid %u\n", cred->cr_uid); | ||
474 | gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); | ||
475 | if (IS_ERR(gss_msg)) { | ||
476 | err = PTR_ERR(gss_msg); | ||
477 | goto out; | ||
478 | } | ||
479 | for (;;) { | ||
480 | prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); | ||
481 | spin_lock(&gss_auth->lock); | ||
482 | if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { | ||
483 | spin_unlock(&gss_auth->lock); | ||
484 | break; | ||
485 | } | ||
486 | spin_unlock(&gss_auth->lock); | ||
487 | if (signalled()) { | ||
488 | err = -ERESTARTSYS; | ||
489 | goto out_intr; | ||
490 | } | ||
491 | schedule(); | ||
492 | } | ||
493 | if (gss_msg->ctx) | ||
494 | gss_cred_set_ctx(cred, gss_get_ctx(gss_msg->ctx)); | ||
495 | else | ||
496 | err = gss_msg->msg.errno; | ||
497 | out_intr: | ||
498 | finish_wait(&gss_msg->waitqueue, &wait); | ||
499 | gss_release_msg(gss_msg); | ||
500 | out: | ||
501 | dprintk("RPC: gss_create_upcall for uid %u result %d\n", cred->cr_uid, err); | ||
502 | return err; | ||
503 | } | ||
504 | |||
505 | static ssize_t | ||
506 | gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, | ||
507 | char __user *dst, size_t buflen) | ||
508 | { | ||
509 | char *data = (char *)msg->data + msg->copied; | ||
510 | ssize_t mlen = msg->len; | ||
511 | ssize_t left; | ||
512 | |||
513 | if (mlen > buflen) | ||
514 | mlen = buflen; | ||
515 | left = copy_to_user(dst, data, mlen); | ||
516 | if (left < 0) { | ||
517 | msg->errno = left; | ||
518 | return left; | ||
519 | } | ||
520 | mlen -= left; | ||
521 | msg->copied += mlen; | ||
522 | msg->errno = 0; | ||
523 | return mlen; | ||
524 | } | ||
525 | |||
526 | #define MSG_BUF_MAXSIZE 1024 | ||
527 | |||
528 | static ssize_t | ||
529 | gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | ||
530 | { | ||
531 | const void *p, *end; | ||
532 | void *buf; | ||
533 | struct rpc_clnt *clnt; | ||
534 | struct gss_auth *gss_auth; | ||
535 | struct rpc_cred *cred; | ||
536 | struct gss_upcall_msg *gss_msg; | ||
537 | struct gss_cl_ctx *ctx; | ||
538 | uid_t uid; | ||
539 | int err = -EFBIG; | ||
540 | |||
541 | if (mlen > MSG_BUF_MAXSIZE) | ||
542 | goto out; | ||
543 | err = -ENOMEM; | ||
544 | buf = kmalloc(mlen, GFP_KERNEL); | ||
545 | if (!buf) | ||
546 | goto out; | ||
547 | |||
548 | clnt = RPC_I(filp->f_dentry->d_inode)->private; | ||
549 | err = -EFAULT; | ||
550 | if (copy_from_user(buf, src, mlen)) | ||
551 | goto err; | ||
552 | |||
553 | end = (const void *)((char *)buf + mlen); | ||
554 | p = simple_get_bytes(buf, end, &uid, sizeof(uid)); | ||
555 | if (IS_ERR(p)) { | ||
556 | err = PTR_ERR(p); | ||
557 | goto err; | ||
558 | } | ||
559 | |||
560 | err = -ENOMEM; | ||
561 | ctx = gss_alloc_context(); | ||
562 | if (ctx == NULL) | ||
563 | goto err; | ||
564 | err = 0; | ||
565 | gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth); | ||
566 | p = gss_fill_context(p, end, ctx, gss_auth->mech); | ||
567 | if (IS_ERR(p)) { | ||
568 | err = PTR_ERR(p); | ||
569 | if (err != -EACCES) | ||
570 | goto err_put_ctx; | ||
571 | } | ||
572 | spin_lock(&gss_auth->lock); | ||
573 | gss_msg = __gss_find_upcall(gss_auth, uid); | ||
574 | if (gss_msg) { | ||
575 | if (err == 0 && gss_msg->ctx == NULL) | ||
576 | gss_msg->ctx = gss_get_ctx(ctx); | ||
577 | gss_msg->msg.errno = err; | ||
578 | __gss_unhash_msg(gss_msg); | ||
579 | spin_unlock(&gss_auth->lock); | ||
580 | gss_release_msg(gss_msg); | ||
581 | } else { | ||
582 | struct auth_cred acred = { .uid = uid }; | ||
583 | spin_unlock(&gss_auth->lock); | ||
584 | cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, 0); | ||
585 | if (IS_ERR(cred)) { | ||
586 | err = PTR_ERR(cred); | ||
587 | goto err_put_ctx; | ||
588 | } | ||
589 | gss_cred_set_ctx(cred, gss_get_ctx(ctx)); | ||
590 | } | ||
591 | gss_put_ctx(ctx); | ||
592 | kfree(buf); | ||
593 | dprintk("RPC: gss_pipe_downcall returning length %Zu\n", mlen); | ||
594 | return mlen; | ||
595 | err_put_ctx: | ||
596 | gss_put_ctx(ctx); | ||
597 | err: | ||
598 | kfree(buf); | ||
599 | out: | ||
600 | dprintk("RPC: gss_pipe_downcall returning %d\n", err); | ||
601 | return err; | ||
602 | } | ||
603 | |||
604 | static void | ||
605 | gss_pipe_release(struct inode *inode) | ||
606 | { | ||
607 | struct rpc_inode *rpci = RPC_I(inode); | ||
608 | struct rpc_clnt *clnt; | ||
609 | struct rpc_auth *auth; | ||
610 | struct gss_auth *gss_auth; | ||
611 | |||
612 | clnt = rpci->private; | ||
613 | auth = clnt->cl_auth; | ||
614 | gss_auth = container_of(auth, struct gss_auth, rpc_auth); | ||
615 | spin_lock(&gss_auth->lock); | ||
616 | while (!list_empty(&gss_auth->upcalls)) { | ||
617 | struct gss_upcall_msg *gss_msg; | ||
618 | |||
619 | gss_msg = list_entry(gss_auth->upcalls.next, | ||
620 | struct gss_upcall_msg, list); | ||
621 | gss_msg->msg.errno = -EPIPE; | ||
622 | atomic_inc(&gss_msg->count); | ||
623 | __gss_unhash_msg(gss_msg); | ||
624 | spin_unlock(&gss_auth->lock); | ||
625 | gss_release_msg(gss_msg); | ||
626 | spin_lock(&gss_auth->lock); | ||
627 | } | ||
628 | spin_unlock(&gss_auth->lock); | ||
629 | } | ||
630 | |||
631 | static void | ||
632 | gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) | ||
633 | { | ||
634 | struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); | ||
635 | static unsigned long ratelimit; | ||
636 | |||
637 | if (msg->errno < 0) { | ||
638 | dprintk("RPC: gss_pipe_destroy_msg releasing msg %p\n", | ||
639 | gss_msg); | ||
640 | atomic_inc(&gss_msg->count); | ||
641 | gss_unhash_msg(gss_msg); | ||
642 | if (msg->errno == -ETIMEDOUT || msg->errno == -EPIPE) { | ||
643 | unsigned long now = jiffies; | ||
644 | if (time_after(now, ratelimit)) { | ||
645 | printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n" | ||
646 | "Please check user daemon is running!\n"); | ||
647 | ratelimit = now + 15*HZ; | ||
648 | } | ||
649 | } | ||
650 | gss_release_msg(gss_msg); | ||
651 | } | ||
652 | } | ||
653 | |||
654 | /* | ||
655 | * NOTE: we have the opportunity to use different | ||
656 | * parameters based on the input flavor (which must be a pseudoflavor) | ||
657 | */ | ||
658 | static struct rpc_auth * | ||
659 | gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) | ||
660 | { | ||
661 | struct gss_auth *gss_auth; | ||
662 | struct rpc_auth * auth; | ||
663 | |||
664 | dprintk("RPC: creating GSS authenticator for client %p\n",clnt); | ||
665 | |||
666 | if (!try_module_get(THIS_MODULE)) | ||
667 | return NULL; | ||
668 | if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) | ||
669 | goto out_dec; | ||
670 | gss_auth->client = clnt; | ||
671 | gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); | ||
672 | if (!gss_auth->mech) { | ||
673 | printk(KERN_WARNING "%s: Pseudoflavor %d not found!", | ||
674 | __FUNCTION__, flavor); | ||
675 | goto err_free; | ||
676 | } | ||
677 | gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); | ||
678 | /* FIXME: Will go away once privacy support is merged in */ | ||
679 | if (gss_auth->service == RPC_GSS_SVC_PRIVACY) | ||
680 | gss_auth->service = RPC_GSS_SVC_INTEGRITY; | ||
681 | INIT_LIST_HEAD(&gss_auth->upcalls); | ||
682 | spin_lock_init(&gss_auth->lock); | ||
683 | auth = &gss_auth->rpc_auth; | ||
684 | auth->au_cslack = GSS_CRED_SLACK >> 2; | ||
685 | auth->au_rslack = GSS_VERF_SLACK >> 2; | ||
686 | auth->au_ops = &authgss_ops; | ||
687 | auth->au_flavor = flavor; | ||
688 | atomic_set(&auth->au_count, 1); | ||
689 | |||
690 | if (rpcauth_init_credcache(auth, GSS_CRED_EXPIRE) < 0) | ||
691 | goto err_put_mech; | ||
692 | |||
693 | snprintf(gss_auth->path, sizeof(gss_auth->path), "%s/%s", | ||
694 | clnt->cl_pathname, | ||
695 | gss_auth->mech->gm_name); | ||
696 | gss_auth->dentry = rpc_mkpipe(gss_auth->path, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); | ||
697 | if (IS_ERR(gss_auth->dentry)) | ||
698 | goto err_put_mech; | ||
699 | |||
700 | return auth; | ||
701 | err_put_mech: | ||
702 | gss_mech_put(gss_auth->mech); | ||
703 | err_free: | ||
704 | kfree(gss_auth); | ||
705 | out_dec: | ||
706 | module_put(THIS_MODULE); | ||
707 | return NULL; | ||
708 | } | ||
709 | |||
710 | static void | ||
711 | gss_destroy(struct rpc_auth *auth) | ||
712 | { | ||
713 | struct gss_auth *gss_auth; | ||
714 | |||
715 | dprintk("RPC: destroying GSS authenticator %p flavor %d\n", | ||
716 | auth, auth->au_flavor); | ||
717 | |||
718 | gss_auth = container_of(auth, struct gss_auth, rpc_auth); | ||
719 | rpc_unlink(gss_auth->path); | ||
720 | gss_mech_put(gss_auth->mech); | ||
721 | |||
722 | rpcauth_free_credcache(auth); | ||
723 | kfree(gss_auth); | ||
724 | module_put(THIS_MODULE); | ||
725 | } | ||
726 | |||
727 | /* gss_destroy_cred (and gss_destroy_ctx) are used to clean up after failure | ||
728 | * to create a new cred or context, so they check that things have been | ||
729 | * allocated before freeing them. */ | ||
730 | static void | ||
731 | gss_destroy_ctx(struct gss_cl_ctx *ctx) | ||
732 | { | ||
733 | dprintk("RPC: gss_destroy_ctx\n"); | ||
734 | |||
735 | if (ctx->gc_gss_ctx) | ||
736 | gss_delete_sec_context(&ctx->gc_gss_ctx); | ||
737 | |||
738 | kfree(ctx->gc_wire_ctx.data); | ||
739 | kfree(ctx); | ||
740 | } | ||
741 | |||
742 | static void | ||
743 | gss_destroy_cred(struct rpc_cred *rc) | ||
744 | { | ||
745 | struct gss_cred *cred = container_of(rc, struct gss_cred, gc_base); | ||
746 | |||
747 | dprintk("RPC: gss_destroy_cred \n"); | ||
748 | |||
749 | if (cred->gc_ctx) | ||
750 | gss_put_ctx(cred->gc_ctx); | ||
751 | kfree(cred); | ||
752 | } | ||
753 | |||
754 | /* | ||
755 | * Lookup RPCSEC_GSS cred for the current process | ||
756 | */ | ||
757 | static struct rpc_cred * | ||
758 | gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags) | ||
759 | { | ||
760 | return rpcauth_lookup_credcache(auth, acred, taskflags); | ||
761 | } | ||
762 | |||
763 | static struct rpc_cred * | ||
764 | gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags) | ||
765 | { | ||
766 | struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); | ||
767 | struct gss_cred *cred = NULL; | ||
768 | int err = -ENOMEM; | ||
769 | |||
770 | dprintk("RPC: gss_create_cred for uid %d, flavor %d\n", | ||
771 | acred->uid, auth->au_flavor); | ||
772 | |||
773 | if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL))) | ||
774 | goto out_err; | ||
775 | |||
776 | memset(cred, 0, sizeof(*cred)); | ||
777 | atomic_set(&cred->gc_count, 1); | ||
778 | cred->gc_uid = acred->uid; | ||
779 | /* | ||
780 | * Note: in order to force a call to call_refresh(), we deliberately | ||
781 | * fail to flag the credential as RPCAUTH_CRED_UPTODATE. | ||
782 | */ | ||
783 | cred->gc_flags = 0; | ||
784 | cred->gc_base.cr_ops = &gss_credops; | ||
785 | cred->gc_service = gss_auth->service; | ||
786 | err = gss_create_upcall(gss_auth, cred); | ||
787 | if (err < 0) | ||
788 | goto out_err; | ||
789 | |||
790 | return &cred->gc_base; | ||
791 | |||
792 | out_err: | ||
793 | dprintk("RPC: gss_create_cred failed with error %d\n", err); | ||
794 | if (cred) gss_destroy_cred(&cred->gc_base); | ||
795 | return ERR_PTR(err); | ||
796 | } | ||
797 | |||
798 | static int | ||
799 | gss_match(struct auth_cred *acred, struct rpc_cred *rc, int taskflags) | ||
800 | { | ||
801 | struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); | ||
802 | |||
803 | /* Don't match with creds that have expired. */ | ||
804 | if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry)) | ||
805 | return 0; | ||
806 | return (rc->cr_uid == acred->uid); | ||
807 | } | ||
808 | |||
809 | /* | ||
810 | * Marshal credentials. | ||
811 | * Maybe we should keep a cached credential for performance reasons. | ||
812 | */ | ||
813 | static u32 * | ||
814 | gss_marshal(struct rpc_task *task, u32 *p) | ||
815 | { | ||
816 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | ||
817 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | ||
818 | gc_base); | ||
819 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | ||
820 | u32 *cred_len; | ||
821 | struct rpc_rqst *req = task->tk_rqstp; | ||
822 | u32 maj_stat = 0; | ||
823 | struct xdr_netobj mic; | ||
824 | struct kvec iov; | ||
825 | struct xdr_buf verf_buf; | ||
826 | |||
827 | dprintk("RPC: %4u gss_marshal\n", task->tk_pid); | ||
828 | |||
829 | *p++ = htonl(RPC_AUTH_GSS); | ||
830 | cred_len = p++; | ||
831 | |||
832 | spin_lock(&ctx->gc_seq_lock); | ||
833 | req->rq_seqno = ctx->gc_seq++; | ||
834 | spin_unlock(&ctx->gc_seq_lock); | ||
835 | |||
836 | *p++ = htonl((u32) RPC_GSS_VERSION); | ||
837 | *p++ = htonl((u32) ctx->gc_proc); | ||
838 | *p++ = htonl((u32) req->rq_seqno); | ||
839 | *p++ = htonl((u32) gss_cred->gc_service); | ||
840 | p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); | ||
841 | *cred_len = htonl((p - (cred_len + 1)) << 2); | ||
842 | |||
843 | /* We compute the checksum for the verifier over the xdr-encoded bytes | ||
844 | * starting with the xid and ending at the end of the credential: */ | ||
845 | iov.iov_base = req->rq_snd_buf.head[0].iov_base; | ||
846 | if (task->tk_client->cl_xprt->stream) | ||
847 | /* See clnt.c:call_header() */ | ||
848 | iov.iov_base += 4; | ||
849 | iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; | ||
850 | xdr_buf_from_iov(&iov, &verf_buf); | ||
851 | |||
852 | /* set verifier flavor*/ | ||
853 | *p++ = htonl(RPC_AUTH_GSS); | ||
854 | |||
855 | mic.data = (u8 *)(p + 1); | ||
856 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, | ||
857 | GSS_C_QOP_DEFAULT, | ||
858 | &verf_buf, &mic); | ||
859 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) { | ||
860 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | ||
861 | } else if (maj_stat != 0) { | ||
862 | printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); | ||
863 | goto out_put_ctx; | ||
864 | } | ||
865 | p = xdr_encode_opaque(p, NULL, mic.len); | ||
866 | gss_put_ctx(ctx); | ||
867 | return p; | ||
868 | out_put_ctx: | ||
869 | gss_put_ctx(ctx); | ||
870 | return NULL; | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * Refresh credentials. XXX - finish | ||
875 | */ | ||
876 | static int | ||
877 | gss_refresh(struct rpc_task *task) | ||
878 | { | ||
879 | |||
880 | if (!gss_cred_is_uptodate_ctx(task->tk_msg.rpc_cred)) | ||
881 | return gss_refresh_upcall(task); | ||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | static u32 * | ||
886 | gss_validate(struct rpc_task *task, u32 *p) | ||
887 | { | ||
888 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | ||
889 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | ||
890 | gc_base); | ||
891 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | ||
892 | u32 seq, qop_state; | ||
893 | struct kvec iov; | ||
894 | struct xdr_buf verf_buf; | ||
895 | struct xdr_netobj mic; | ||
896 | u32 flav,len; | ||
897 | u32 maj_stat; | ||
898 | |||
899 | dprintk("RPC: %4u gss_validate\n", task->tk_pid); | ||
900 | |||
901 | flav = ntohl(*p++); | ||
902 | if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) | ||
903 | goto out_bad; | ||
904 | if (flav != RPC_AUTH_GSS) | ||
905 | goto out_bad; | ||
906 | seq = htonl(task->tk_rqstp->rq_seqno); | ||
907 | iov.iov_base = &seq; | ||
908 | iov.iov_len = sizeof(seq); | ||
909 | xdr_buf_from_iov(&iov, &verf_buf); | ||
910 | mic.data = (u8 *)p; | ||
911 | mic.len = len; | ||
912 | |||
913 | maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state); | ||
914 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | ||
915 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | ||
916 | if (maj_stat) | ||
917 | goto out_bad; | ||
918 | switch (gss_cred->gc_service) { | ||
919 | case RPC_GSS_SVC_NONE: | ||
920 | /* verifier data, flavor, length: */ | ||
921 | task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; | ||
922 | break; | ||
923 | case RPC_GSS_SVC_INTEGRITY: | ||
924 | /* verifier data, flavor, length, length, sequence number: */ | ||
925 | task->tk_auth->au_rslack = XDR_QUADLEN(len) + 4; | ||
926 | break; | ||
927 | case RPC_GSS_SVC_PRIVACY: | ||
928 | goto out_bad; | ||
929 | } | ||
930 | gss_put_ctx(ctx); | ||
931 | dprintk("RPC: %4u GSS gss_validate: gss_verify_mic succeeded.\n", | ||
932 | task->tk_pid); | ||
933 | return p + XDR_QUADLEN(len); | ||
934 | out_bad: | ||
935 | gss_put_ctx(ctx); | ||
936 | dprintk("RPC: %4u gss_validate failed.\n", task->tk_pid); | ||
937 | return NULL; | ||
938 | } | ||
939 | |||
940 | static inline int | ||
941 | gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | ||
942 | kxdrproc_t encode, struct rpc_rqst *rqstp, u32 *p, void *obj) | ||
943 | { | ||
944 | struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | ||
945 | struct xdr_buf integ_buf; | ||
946 | u32 *integ_len = NULL; | ||
947 | struct xdr_netobj mic; | ||
948 | u32 offset, *q; | ||
949 | struct kvec *iov; | ||
950 | u32 maj_stat = 0; | ||
951 | int status = -EIO; | ||
952 | |||
953 | integ_len = p++; | ||
954 | offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | ||
955 | *p++ = htonl(rqstp->rq_seqno); | ||
956 | |||
957 | status = encode(rqstp, p, obj); | ||
958 | if (status) | ||
959 | return status; | ||
960 | |||
961 | if (xdr_buf_subsegment(snd_buf, &integ_buf, | ||
962 | offset, snd_buf->len - offset)) | ||
963 | return status; | ||
964 | *integ_len = htonl(integ_buf.len); | ||
965 | |||
966 | /* guess whether we're in the head or the tail: */ | ||
967 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) | ||
968 | iov = snd_buf->tail; | ||
969 | else | ||
970 | iov = snd_buf->head; | ||
971 | p = iov->iov_base + iov->iov_len; | ||
972 | mic.data = (u8 *)(p + 1); | ||
973 | |||
974 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, | ||
975 | GSS_C_QOP_DEFAULT, &integ_buf, &mic); | ||
976 | status = -EIO; /* XXX? */ | ||
977 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | ||
978 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | ||
979 | else if (maj_stat) | ||
980 | return status; | ||
981 | q = xdr_encode_opaque(p, NULL, mic.len); | ||
982 | |||
983 | offset = (u8 *)q - (u8 *)p; | ||
984 | iov->iov_len += offset; | ||
985 | snd_buf->len += offset; | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | static int | ||
990 | gss_wrap_req(struct rpc_task *task, | ||
991 | kxdrproc_t encode, void *rqstp, u32 *p, void *obj) | ||
992 | { | ||
993 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | ||
994 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | ||
995 | gc_base); | ||
996 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | ||
997 | int status = -EIO; | ||
998 | |||
999 | dprintk("RPC: %4u gss_wrap_req\n", task->tk_pid); | ||
1000 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) { | ||
1001 | /* The spec seems a little ambiguous here, but I think that not | ||
1002 | * wrapping context destruction requests makes the most sense. | ||
1003 | */ | ||
1004 | status = encode(rqstp, p, obj); | ||
1005 | goto out; | ||
1006 | } | ||
1007 | switch (gss_cred->gc_service) { | ||
1008 | case RPC_GSS_SVC_NONE: | ||
1009 | status = encode(rqstp, p, obj); | ||
1010 | break; | ||
1011 | case RPC_GSS_SVC_INTEGRITY: | ||
1012 | status = gss_wrap_req_integ(cred, ctx, encode, | ||
1013 | rqstp, p, obj); | ||
1014 | break; | ||
1015 | case RPC_GSS_SVC_PRIVACY: | ||
1016 | break; | ||
1017 | } | ||
1018 | out: | ||
1019 | gss_put_ctx(ctx); | ||
1020 | dprintk("RPC: %4u gss_wrap_req returning %d\n", task->tk_pid, status); | ||
1021 | return status; | ||
1022 | } | ||
1023 | |||
1024 | static inline int | ||
1025 | gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | ||
1026 | struct rpc_rqst *rqstp, u32 **p) | ||
1027 | { | ||
1028 | struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; | ||
1029 | struct xdr_buf integ_buf; | ||
1030 | struct xdr_netobj mic; | ||
1031 | u32 data_offset, mic_offset; | ||
1032 | u32 integ_len; | ||
1033 | u32 maj_stat; | ||
1034 | int status = -EIO; | ||
1035 | |||
1036 | integ_len = ntohl(*(*p)++); | ||
1037 | if (integ_len & 3) | ||
1038 | return status; | ||
1039 | data_offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base; | ||
1040 | mic_offset = integ_len + data_offset; | ||
1041 | if (mic_offset > rcv_buf->len) | ||
1042 | return status; | ||
1043 | if (ntohl(*(*p)++) != rqstp->rq_seqno) | ||
1044 | return status; | ||
1045 | |||
1046 | if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, | ||
1047 | mic_offset - data_offset)) | ||
1048 | return status; | ||
1049 | |||
1050 | if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) | ||
1051 | return status; | ||
1052 | |||
1053 | maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, | ||
1054 | &mic, NULL); | ||
1055 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | ||
1056 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | ||
1057 | if (maj_stat != GSS_S_COMPLETE) | ||
1058 | return status; | ||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | static int | ||
1063 | gss_unwrap_resp(struct rpc_task *task, | ||
1064 | kxdrproc_t decode, void *rqstp, u32 *p, void *obj) | ||
1065 | { | ||
1066 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | ||
1067 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | ||
1068 | gc_base); | ||
1069 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | ||
1070 | int status = -EIO; | ||
1071 | |||
1072 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) | ||
1073 | goto out_decode; | ||
1074 | switch (gss_cred->gc_service) { | ||
1075 | case RPC_GSS_SVC_NONE: | ||
1076 | break; | ||
1077 | case RPC_GSS_SVC_INTEGRITY: | ||
1078 | status = gss_unwrap_resp_integ(cred, ctx, rqstp, &p); | ||
1079 | if (status) | ||
1080 | goto out; | ||
1081 | break; | ||
1082 | case RPC_GSS_SVC_PRIVACY: | ||
1083 | break; | ||
1084 | } | ||
1085 | out_decode: | ||
1086 | status = decode(rqstp, p, obj); | ||
1087 | out: | ||
1088 | gss_put_ctx(ctx); | ||
1089 | dprintk("RPC: %4u gss_unwrap_resp returning %d\n", task->tk_pid, | ||
1090 | status); | ||
1091 | return status; | ||
1092 | } | ||
1093 | |||
1094 | static struct rpc_authops authgss_ops = { | ||
1095 | .owner = THIS_MODULE, | ||
1096 | .au_flavor = RPC_AUTH_GSS, | ||
1097 | #ifdef RPC_DEBUG | ||
1098 | .au_name = "RPCSEC_GSS", | ||
1099 | #endif | ||
1100 | .create = gss_create, | ||
1101 | .destroy = gss_destroy, | ||
1102 | .lookup_cred = gss_lookup_cred, | ||
1103 | .crcreate = gss_create_cred | ||
1104 | }; | ||
1105 | |||
1106 | static struct rpc_credops gss_credops = { | ||
1107 | .cr_name = "AUTH_GSS", | ||
1108 | .crdestroy = gss_destroy_cred, | ||
1109 | .crmatch = gss_match, | ||
1110 | .crmarshal = gss_marshal, | ||
1111 | .crrefresh = gss_refresh, | ||
1112 | .crvalidate = gss_validate, | ||
1113 | .crwrap_req = gss_wrap_req, | ||
1114 | .crunwrap_resp = gss_unwrap_resp, | ||
1115 | }; | ||
1116 | |||
1117 | static struct rpc_pipe_ops gss_upcall_ops = { | ||
1118 | .upcall = gss_pipe_upcall, | ||
1119 | .downcall = gss_pipe_downcall, | ||
1120 | .destroy_msg = gss_pipe_destroy_msg, | ||
1121 | .release_pipe = gss_pipe_release, | ||
1122 | }; | ||
1123 | |||
1124 | /* | ||
1125 | * Initialize RPCSEC_GSS module | ||
1126 | */ | ||
1127 | static int __init init_rpcsec_gss(void) | ||
1128 | { | ||
1129 | int err = 0; | ||
1130 | |||
1131 | err = rpcauth_register(&authgss_ops); | ||
1132 | if (err) | ||
1133 | goto out; | ||
1134 | err = gss_svc_init(); | ||
1135 | if (err) | ||
1136 | goto out_unregister; | ||
1137 | return 0; | ||
1138 | out_unregister: | ||
1139 | rpcauth_unregister(&authgss_ops); | ||
1140 | out: | ||
1141 | return err; | ||
1142 | } | ||
1143 | |||
1144 | static void __exit exit_rpcsec_gss(void) | ||
1145 | { | ||
1146 | gss_svc_shutdown(); | ||
1147 | rpcauth_unregister(&authgss_ops); | ||
1148 | } | ||
1149 | |||
1150 | MODULE_LICENSE("GPL"); | ||
1151 | module_init(init_rpcsec_gss) | ||
1152 | module_exit(exit_rpcsec_gss) | ||
diff --git a/net/sunrpc/auth_gss/gss_generic_token.c b/net/sunrpc/auth_gss/gss_generic_token.c new file mode 100644 index 000000000000..826df44e7fca --- /dev/null +++ b/net/sunrpc/auth_gss/gss_generic_token.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_generic_token.c | ||
3 | * | ||
4 | * Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic/util_token.c | ||
5 | * | ||
6 | * Copyright (c) 2000 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Andy Adamson <andros@umich.edu> | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * Copyright 1993 by OpenVision Technologies, Inc. | ||
14 | * | ||
15 | * Permission to use, copy, modify, distribute, and sell this software | ||
16 | * and its documentation for any purpose is hereby granted without fee, | ||
17 | * provided that the above copyright notice appears in all copies and | ||
18 | * that both that copyright notice and this permission notice appear in | ||
19 | * supporting documentation, and that the name of OpenVision not be used | ||
20 | * in advertising or publicity pertaining to distribution of the software | ||
21 | * without specific, written prior permission. OpenVision makes no | ||
22 | * representations about the suitability of this software for any | ||
23 | * purpose. It is provided "as is" without express or implied warranty. | ||
24 | * | ||
25 | * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
26 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
27 | * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
28 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | ||
29 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
30 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
31 | * PERFORMANCE OF THIS SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | #include <linux/types.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/string.h> | ||
38 | #include <linux/sunrpc/sched.h> | ||
39 | #include <linux/sunrpc/gss_asn1.h> | ||
40 | |||
41 | |||
42 | #ifdef RPC_DEBUG | ||
43 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
44 | #endif | ||
45 | |||
46 | |||
47 | /* TWRITE_STR from gssapiP_generic.h */ | ||
48 | #define TWRITE_STR(ptr, str, len) \ | ||
49 | memcpy((ptr), (char *) (str), (len)); \ | ||
50 | (ptr) += (len); | ||
51 | |||
52 | /* XXXX this code currently makes the assumption that a mech oid will | ||
53 | never be longer than 127 bytes. This assumption is not inherent in | ||
54 | the interfaces, so the code can be fixed if the OSI namespace | ||
55 | balloons unexpectedly. */ | ||
56 | |||
57 | /* Each token looks like this: | ||
58 | |||
59 | 0x60 tag for APPLICATION 0, SEQUENCE | ||
60 | (constructed, definite-length) | ||
61 | <length> possible multiple bytes, need to parse/generate | ||
62 | 0x06 tag for OBJECT IDENTIFIER | ||
63 | <moid_length> compile-time constant string (assume 1 byte) | ||
64 | <moid_bytes> compile-time constant string | ||
65 | <inner_bytes> the ANY containing the application token | ||
66 | bytes 0,1 are the token type | ||
67 | bytes 2,n are the token data | ||
68 | |||
69 | For the purposes of this abstraction, the token "header" consists of | ||
70 | the sequence tag and length octets, the mech OID DER encoding, and the | ||
71 | first two inner bytes, which indicate the token type. The token | ||
72 | "body" consists of everything else. | ||
73 | |||
74 | */ | ||
75 | |||
76 | static int | ||
77 | der_length_size( int length) | ||
78 | { | ||
79 | if (length < (1<<7)) | ||
80 | return(1); | ||
81 | else if (length < (1<<8)) | ||
82 | return(2); | ||
83 | #if (SIZEOF_INT == 2) | ||
84 | else | ||
85 | return(3); | ||
86 | #else | ||
87 | else if (length < (1<<16)) | ||
88 | return(3); | ||
89 | else if (length < (1<<24)) | ||
90 | return(4); | ||
91 | else | ||
92 | return(5); | ||
93 | #endif | ||
94 | } | ||
95 | |||
96 | static void | ||
97 | der_write_length(unsigned char **buf, int length) | ||
98 | { | ||
99 | if (length < (1<<7)) { | ||
100 | *(*buf)++ = (unsigned char) length; | ||
101 | } else { | ||
102 | *(*buf)++ = (unsigned char) (der_length_size(length)+127); | ||
103 | #if (SIZEOF_INT > 2) | ||
104 | if (length >= (1<<24)) | ||
105 | *(*buf)++ = (unsigned char) (length>>24); | ||
106 | if (length >= (1<<16)) | ||
107 | *(*buf)++ = (unsigned char) ((length>>16)&0xff); | ||
108 | #endif | ||
109 | if (length >= (1<<8)) | ||
110 | *(*buf)++ = (unsigned char) ((length>>8)&0xff); | ||
111 | *(*buf)++ = (unsigned char) (length&0xff); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /* returns decoded length, or < 0 on failure. Advances buf and | ||
116 | decrements bufsize */ | ||
117 | |||
118 | static int | ||
119 | der_read_length(unsigned char **buf, int *bufsize) | ||
120 | { | ||
121 | unsigned char sf; | ||
122 | int ret; | ||
123 | |||
124 | if (*bufsize < 1) | ||
125 | return(-1); | ||
126 | sf = *(*buf)++; | ||
127 | (*bufsize)--; | ||
128 | if (sf & 0x80) { | ||
129 | if ((sf &= 0x7f) > ((*bufsize)-1)) | ||
130 | return(-1); | ||
131 | if (sf > SIZEOF_INT) | ||
132 | return (-1); | ||
133 | ret = 0; | ||
134 | for (; sf; sf--) { | ||
135 | ret = (ret<<8) + (*(*buf)++); | ||
136 | (*bufsize)--; | ||
137 | } | ||
138 | } else { | ||
139 | ret = sf; | ||
140 | } | ||
141 | |||
142 | return(ret); | ||
143 | } | ||
144 | |||
145 | /* returns the length of a token, given the mech oid and the body size */ | ||
146 | |||
147 | int | ||
148 | g_token_size(struct xdr_netobj *mech, unsigned int body_size) | ||
149 | { | ||
150 | /* set body_size to sequence contents size */ | ||
151 | body_size += 4 + (int) mech->len; /* NEED overflow check */ | ||
152 | return(1 + der_length_size(body_size) + body_size); | ||
153 | } | ||
154 | |||
155 | EXPORT_SYMBOL(g_token_size); | ||
156 | |||
157 | /* fills in a buffer with the token header. The buffer is assumed to | ||
158 | be the right size. buf is advanced past the token header */ | ||
159 | |||
160 | void | ||
161 | g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf) | ||
162 | { | ||
163 | *(*buf)++ = 0x60; | ||
164 | der_write_length(buf, 4 + mech->len + body_size); | ||
165 | *(*buf)++ = 0x06; | ||
166 | *(*buf)++ = (unsigned char) mech->len; | ||
167 | TWRITE_STR(*buf, mech->data, ((int) mech->len)); | ||
168 | } | ||
169 | |||
170 | EXPORT_SYMBOL(g_make_token_header); | ||
171 | |||
172 | /* | ||
173 | * Given a buffer containing a token, reads and verifies the token, | ||
174 | * leaving buf advanced past the token header, and setting body_size | ||
175 | * to the number of remaining bytes. Returns 0 on success, | ||
176 | * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the | ||
177 | * mechanism in the token does not match the mech argument. buf and | ||
178 | * *body_size are left unmodified on error. | ||
179 | */ | ||
180 | u32 | ||
181 | g_verify_token_header(struct xdr_netobj *mech, int *body_size, | ||
182 | unsigned char **buf_in, int toksize) | ||
183 | { | ||
184 | unsigned char *buf = *buf_in; | ||
185 | int seqsize; | ||
186 | struct xdr_netobj toid; | ||
187 | int ret = 0; | ||
188 | |||
189 | if ((toksize-=1) < 0) | ||
190 | return(G_BAD_TOK_HEADER); | ||
191 | if (*buf++ != 0x60) | ||
192 | return(G_BAD_TOK_HEADER); | ||
193 | |||
194 | if ((seqsize = der_read_length(&buf, &toksize)) < 0) | ||
195 | return(G_BAD_TOK_HEADER); | ||
196 | |||
197 | if (seqsize != toksize) | ||
198 | return(G_BAD_TOK_HEADER); | ||
199 | |||
200 | if ((toksize-=1) < 0) | ||
201 | return(G_BAD_TOK_HEADER); | ||
202 | if (*buf++ != 0x06) | ||
203 | return(G_BAD_TOK_HEADER); | ||
204 | |||
205 | if ((toksize-=1) < 0) | ||
206 | return(G_BAD_TOK_HEADER); | ||
207 | toid.len = *buf++; | ||
208 | |||
209 | if ((toksize-=toid.len) < 0) | ||
210 | return(G_BAD_TOK_HEADER); | ||
211 | toid.data = buf; | ||
212 | buf+=toid.len; | ||
213 | |||
214 | if (! g_OID_equal(&toid, mech)) | ||
215 | ret = G_WRONG_MECH; | ||
216 | |||
217 | /* G_WRONG_MECH is not returned immediately because it's more important | ||
218 | to return G_BAD_TOK_HEADER if the token header is in fact bad */ | ||
219 | |||
220 | if ((toksize-=2) < 0) | ||
221 | return(G_BAD_TOK_HEADER); | ||
222 | |||
223 | if (ret) | ||
224 | return(ret); | ||
225 | |||
226 | if (!ret) { | ||
227 | *buf_in = buf; | ||
228 | *body_size = toksize; | ||
229 | } | ||
230 | |||
231 | return(ret); | ||
232 | } | ||
233 | |||
234 | EXPORT_SYMBOL(g_verify_token_header); | ||
235 | |||
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c new file mode 100644 index 000000000000..24c21f2a33a7 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c | |||
@@ -0,0 +1,209 @@ | |||
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 | /* checksum the plaintext data and hdrlen bytes of the token header */ | ||
143 | s32 | ||
144 | make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body, | ||
145 | struct xdr_netobj *cksum) | ||
146 | { | ||
147 | char *cksumname; | ||
148 | struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */ | ||
149 | struct scatterlist sg[1]; | ||
150 | u32 code = GSS_S_FAILURE; | ||
151 | int len, thislen, offset; | ||
152 | int i; | ||
153 | |||
154 | switch (cksumtype) { | ||
155 | case CKSUMTYPE_RSA_MD5: | ||
156 | cksumname = "md5"; | ||
157 | break; | ||
158 | default: | ||
159 | dprintk("RPC: krb5_make_checksum:" | ||
160 | " unsupported checksum %d", cksumtype); | ||
161 | goto out; | ||
162 | } | ||
163 | if (!(tfm = crypto_alloc_tfm(cksumname, 0))) | ||
164 | goto out; | ||
165 | cksum->len = crypto_tfm_alg_digestsize(tfm); | ||
166 | if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) | ||
167 | goto out; | ||
168 | |||
169 | crypto_digest_init(tfm); | ||
170 | buf_to_sg(sg, header, hdrlen); | ||
171 | crypto_digest_update(tfm, sg, 1); | ||
172 | if (body->head[0].iov_len) { | ||
173 | buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len); | ||
174 | crypto_digest_update(tfm, sg, 1); | ||
175 | } | ||
176 | |||
177 | len = body->page_len; | ||
178 | if (len != 0) { | ||
179 | offset = body->page_base & (PAGE_CACHE_SIZE - 1); | ||
180 | i = body->page_base >> PAGE_CACHE_SHIFT; | ||
181 | thislen = PAGE_CACHE_SIZE - offset; | ||
182 | do { | ||
183 | if (thislen > len) | ||
184 | thislen = len; | ||
185 | sg->page = body->pages[i]; | ||
186 | sg->offset = offset; | ||
187 | sg->length = thislen; | ||
188 | kmap(sg->page); /* XXX kmap_atomic? */ | ||
189 | crypto_digest_update(tfm, sg, 1); | ||
190 | kunmap(sg->page); | ||
191 | len -= thislen; | ||
192 | i++; | ||
193 | offset = 0; | ||
194 | thislen = PAGE_CACHE_SIZE; | ||
195 | } while(len != 0); | ||
196 | } | ||
197 | if (body->tail[0].iov_len) { | ||
198 | buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len); | ||
199 | crypto_digest_update(tfm, sg, 1); | ||
200 | } | ||
201 | crypto_digest_final(tfm, cksum->data); | ||
202 | code = 0; | ||
203 | out: | ||
204 | if (tfm) | ||
205 | crypto_free_tfm(tfm); | ||
206 | return code; | ||
207 | } | ||
208 | |||
209 | EXPORT_SYMBOL(make_checksum); | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c new file mode 100644 index 000000000000..cf726510df8e --- /dev/null +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c | |||
@@ -0,0 +1,275 @@ | |||
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/gss_krb5.h> | ||
44 | #include <linux/sunrpc/xdr.h> | ||
45 | #include <linux/crypto.h> | ||
46 | |||
47 | #ifdef RPC_DEBUG | ||
48 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
49 | #endif | ||
50 | |||
51 | static const void * | ||
52 | simple_get_bytes(const void *p, const void *end, void *res, int len) | ||
53 | { | ||
54 | const void *q = (const void *)((const char *)p + len); | ||
55 | if (unlikely(q > end || q < p)) | ||
56 | return ERR_PTR(-EFAULT); | ||
57 | memcpy(res, p, len); | ||
58 | return q; | ||
59 | } | ||
60 | |||
61 | static const void * | ||
62 | simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res) | ||
63 | { | ||
64 | const void *q; | ||
65 | unsigned int len; | ||
66 | |||
67 | p = simple_get_bytes(p, end, &len, sizeof(len)); | ||
68 | if (IS_ERR(p)) | ||
69 | return p; | ||
70 | q = (const void *)((const char *)p + len); | ||
71 | if (unlikely(q > end || q < p)) | ||
72 | return ERR_PTR(-EFAULT); | ||
73 | res->data = kmalloc(len, GFP_KERNEL); | ||
74 | if (unlikely(res->data == NULL)) | ||
75 | return ERR_PTR(-ENOMEM); | ||
76 | memcpy(res->data, p, len); | ||
77 | res->len = len; | ||
78 | return q; | ||
79 | } | ||
80 | |||
81 | static inline const void * | ||
82 | get_key(const void *p, const void *end, struct crypto_tfm **res) | ||
83 | { | ||
84 | struct xdr_netobj key; | ||
85 | int alg, alg_mode; | ||
86 | char *alg_name; | ||
87 | |||
88 | p = simple_get_bytes(p, end, &alg, sizeof(alg)); | ||
89 | if (IS_ERR(p)) | ||
90 | goto out_err; | ||
91 | p = simple_get_netobj(p, end, &key); | ||
92 | if (IS_ERR(p)) | ||
93 | goto out_err; | ||
94 | |||
95 | switch (alg) { | ||
96 | case ENCTYPE_DES_CBC_RAW: | ||
97 | alg_name = "des"; | ||
98 | alg_mode = CRYPTO_TFM_MODE_CBC; | ||
99 | break; | ||
100 | default: | ||
101 | dprintk("RPC: get_key: unsupported algorithm %d\n", alg); | ||
102 | goto out_err_free_key; | ||
103 | } | ||
104 | if (!(*res = crypto_alloc_tfm(alg_name, alg_mode))) | ||
105 | goto out_err_free_key; | ||
106 | if (crypto_cipher_setkey(*res, key.data, key.len)) | ||
107 | goto out_err_free_tfm; | ||
108 | |||
109 | kfree(key.data); | ||
110 | return p; | ||
111 | |||
112 | out_err_free_tfm: | ||
113 | crypto_free_tfm(*res); | ||
114 | out_err_free_key: | ||
115 | kfree(key.data); | ||
116 | p = ERR_PTR(-EINVAL); | ||
117 | out_err: | ||
118 | return p; | ||
119 | } | ||
120 | |||
121 | static int | ||
122 | gss_import_sec_context_kerberos(const void *p, | ||
123 | size_t len, | ||
124 | struct gss_ctx *ctx_id) | ||
125 | { | ||
126 | const void *end = (const void *)((const char *)p + len); | ||
127 | struct krb5_ctx *ctx; | ||
128 | |||
129 | if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL))) | ||
130 | goto out_err; | ||
131 | memset(ctx, 0, sizeof(*ctx)); | ||
132 | |||
133 | p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate)); | ||
134 | if (IS_ERR(p)) | ||
135 | goto out_err_free_ctx; | ||
136 | p = simple_get_bytes(p, end, &ctx->seed_init, sizeof(ctx->seed_init)); | ||
137 | if (IS_ERR(p)) | ||
138 | goto out_err_free_ctx; | ||
139 | p = simple_get_bytes(p, end, ctx->seed, sizeof(ctx->seed)); | ||
140 | if (IS_ERR(p)) | ||
141 | goto out_err_free_ctx; | ||
142 | p = simple_get_bytes(p, end, &ctx->signalg, sizeof(ctx->signalg)); | ||
143 | if (IS_ERR(p)) | ||
144 | goto out_err_free_ctx; | ||
145 | p = simple_get_bytes(p, end, &ctx->sealalg, sizeof(ctx->sealalg)); | ||
146 | if (IS_ERR(p)) | ||
147 | goto out_err_free_ctx; | ||
148 | p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime)); | ||
149 | if (IS_ERR(p)) | ||
150 | goto out_err_free_ctx; | ||
151 | p = simple_get_bytes(p, end, &ctx->seq_send, sizeof(ctx->seq_send)); | ||
152 | if (IS_ERR(p)) | ||
153 | goto out_err_free_ctx; | ||
154 | p = simple_get_netobj(p, end, &ctx->mech_used); | ||
155 | if (IS_ERR(p)) | ||
156 | goto out_err_free_ctx; | ||
157 | p = get_key(p, end, &ctx->enc); | ||
158 | if (IS_ERR(p)) | ||
159 | goto out_err_free_mech; | ||
160 | p = get_key(p, end, &ctx->seq); | ||
161 | if (IS_ERR(p)) | ||
162 | goto out_err_free_key1; | ||
163 | if (p != end) { | ||
164 | p = ERR_PTR(-EFAULT); | ||
165 | goto out_err_free_key2; | ||
166 | } | ||
167 | |||
168 | ctx_id->internal_ctx_id = ctx; | ||
169 | dprintk("RPC: Succesfully imported new context.\n"); | ||
170 | return 0; | ||
171 | |||
172 | out_err_free_key2: | ||
173 | crypto_free_tfm(ctx->seq); | ||
174 | out_err_free_key1: | ||
175 | crypto_free_tfm(ctx->enc); | ||
176 | out_err_free_mech: | ||
177 | kfree(ctx->mech_used.data); | ||
178 | out_err_free_ctx: | ||
179 | kfree(ctx); | ||
180 | out_err: | ||
181 | return PTR_ERR(p); | ||
182 | } | ||
183 | |||
184 | static void | ||
185 | gss_delete_sec_context_kerberos(void *internal_ctx) { | ||
186 | struct krb5_ctx *kctx = internal_ctx; | ||
187 | |||
188 | if (kctx->seq) | ||
189 | crypto_free_tfm(kctx->seq); | ||
190 | if (kctx->enc) | ||
191 | crypto_free_tfm(kctx->enc); | ||
192 | if (kctx->mech_used.data) | ||
193 | kfree(kctx->mech_used.data); | ||
194 | kfree(kctx); | ||
195 | } | ||
196 | |||
197 | static u32 | ||
198 | gss_verify_mic_kerberos(struct gss_ctx *ctx, | ||
199 | struct xdr_buf *message, | ||
200 | struct xdr_netobj *mic_token, | ||
201 | u32 *qstate) { | ||
202 | u32 maj_stat = 0; | ||
203 | int qop_state; | ||
204 | struct krb5_ctx *kctx = ctx->internal_ctx_id; | ||
205 | |||
206 | maj_stat = krb5_read_token(kctx, mic_token, message, &qop_state, | ||
207 | KG_TOK_MIC_MSG); | ||
208 | if (!maj_stat && qop_state) | ||
209 | *qstate = qop_state; | ||
210 | |||
211 | dprintk("RPC: gss_verify_mic_kerberos returning %d\n", maj_stat); | ||
212 | return maj_stat; | ||
213 | } | ||
214 | |||
215 | static u32 | ||
216 | gss_get_mic_kerberos(struct gss_ctx *ctx, | ||
217 | u32 qop, | ||
218 | struct xdr_buf *message, | ||
219 | struct xdr_netobj *mic_token) { | ||
220 | u32 err = 0; | ||
221 | struct krb5_ctx *kctx = ctx->internal_ctx_id; | ||
222 | |||
223 | err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG); | ||
224 | |||
225 | dprintk("RPC: gss_get_mic_kerberos returning %d\n",err); | ||
226 | |||
227 | return err; | ||
228 | } | ||
229 | |||
230 | static struct gss_api_ops gss_kerberos_ops = { | ||
231 | .gss_import_sec_context = gss_import_sec_context_kerberos, | ||
232 | .gss_get_mic = gss_get_mic_kerberos, | ||
233 | .gss_verify_mic = gss_verify_mic_kerberos, | ||
234 | .gss_delete_sec_context = gss_delete_sec_context_kerberos, | ||
235 | }; | ||
236 | |||
237 | static struct pf_desc gss_kerberos_pfs[] = { | ||
238 | [0] = { | ||
239 | .pseudoflavor = RPC_AUTH_GSS_KRB5, | ||
240 | .service = RPC_GSS_SVC_NONE, | ||
241 | .name = "krb5", | ||
242 | }, | ||
243 | [1] = { | ||
244 | .pseudoflavor = RPC_AUTH_GSS_KRB5I, | ||
245 | .service = RPC_GSS_SVC_INTEGRITY, | ||
246 | .name = "krb5i", | ||
247 | }, | ||
248 | }; | ||
249 | |||
250 | static struct gss_api_mech gss_kerberos_mech = { | ||
251 | .gm_name = "krb5", | ||
252 | .gm_owner = THIS_MODULE, | ||
253 | .gm_ops = &gss_kerberos_ops, | ||
254 | .gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs), | ||
255 | .gm_pfs = gss_kerberos_pfs, | ||
256 | }; | ||
257 | |||
258 | static int __init init_kerberos_module(void) | ||
259 | { | ||
260 | int status; | ||
261 | |||
262 | status = gss_mech_register(&gss_kerberos_mech); | ||
263 | if (status) | ||
264 | printk("Failed to register kerberos gss mechanism!\n"); | ||
265 | return status; | ||
266 | } | ||
267 | |||
268 | static void __exit cleanup_kerberos_module(void) | ||
269 | { | ||
270 | gss_mech_unregister(&gss_kerberos_mech); | ||
271 | } | ||
272 | |||
273 | MODULE_LICENSE("GPL"); | ||
274 | module_init(init_kerberos_module); | ||
275 | module_exit(cleanup_kerberos_module); | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c new file mode 100644 index 000000000000..afeeb8715a77 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_krb5_seal.c | ||
3 | * | ||
4 | * Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/k5seal.c | ||
5 | * | ||
6 | * Copyright (c) 2000 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Andy Adamson <andros@umich.edu> | ||
10 | * J. Bruce Fields <bfields@umich.edu> | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Copyright 1993 by OpenVision Technologies, Inc. | ||
15 | * | ||
16 | * Permission to use, copy, modify, distribute, and sell this software | ||
17 | * and its documentation for any purpose is hereby granted without fee, | ||
18 | * provided that the above copyright notice appears in all copies and | ||
19 | * that both that copyright notice and this permission notice appear in | ||
20 | * supporting documentation, and that the name of OpenVision not be used | ||
21 | * in advertising or publicity pertaining to distribution of the software | ||
22 | * without specific, written prior permission. OpenVision makes no | ||
23 | * representations about the suitability of this software for any | ||
24 | * purpose. It is provided "as is" without express or implied warranty. | ||
25 | * | ||
26 | * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
27 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
28 | * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
29 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | ||
30 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
31 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
32 | * PERFORMANCE OF THIS SOFTWARE. | ||
33 | */ | ||
34 | |||
35 | /* | ||
36 | * Copyright (C) 1998 by the FundsXpress, INC. | ||
37 | * | ||
38 | * All rights reserved. | ||
39 | * | ||
40 | * Export of this software from the United States of America may require | ||
41 | * a specific license from the United States Government. It is the | ||
42 | * responsibility of any person or organization contemplating export to | ||
43 | * obtain such a license before exporting. | ||
44 | * | ||
45 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and | ||
46 | * distribute this software and its documentation for any purpose and | ||
47 | * without fee is hereby granted, provided that the above copyright | ||
48 | * notice appear in all copies and that both that copyright notice and | ||
49 | * this permission notice appear in supporting documentation, and that | ||
50 | * the name of FundsXpress. not be used in advertising or publicity pertaining | ||
51 | * to distribution of the software without specific, written prior | ||
52 | * permission. FundsXpress makes no representations about the suitability of | ||
53 | * this software for any purpose. It is provided "as is" without express | ||
54 | * or implied warranty. | ||
55 | * | ||
56 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | ||
57 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | ||
58 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
59 | */ | ||
60 | |||
61 | #include <linux/types.h> | ||
62 | #include <linux/slab.h> | ||
63 | #include <linux/jiffies.h> | ||
64 | #include <linux/sunrpc/gss_krb5.h> | ||
65 | #include <linux/random.h> | ||
66 | #include <asm/scatterlist.h> | ||
67 | #include <linux/crypto.h> | ||
68 | |||
69 | #ifdef RPC_DEBUG | ||
70 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
71 | #endif | ||
72 | |||
73 | static inline int | ||
74 | gss_krb5_padding(int blocksize, int length) { | ||
75 | /* Most of the code is block-size independent but in practice we | ||
76 | * use only 8: */ | ||
77 | BUG_ON(blocksize != 8); | ||
78 | return 8 - (length & 7); | ||
79 | } | ||
80 | |||
81 | u32 | ||
82 | krb5_make_token(struct krb5_ctx *ctx, int qop_req, | ||
83 | struct xdr_buf *text, struct xdr_netobj *token, | ||
84 | int toktype) | ||
85 | { | ||
86 | s32 checksum_type; | ||
87 | struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; | ||
88 | int blocksize = 0, tmsglen; | ||
89 | unsigned char *ptr, *krb5_hdr, *msg_start; | ||
90 | s32 now; | ||
91 | |||
92 | dprintk("RPC: gss_krb5_seal\n"); | ||
93 | |||
94 | now = get_seconds(); | ||
95 | |||
96 | if (qop_req != 0) | ||
97 | goto out_err; | ||
98 | |||
99 | switch (ctx->signalg) { | ||
100 | case SGN_ALG_DES_MAC_MD5: | ||
101 | checksum_type = CKSUMTYPE_RSA_MD5; | ||
102 | break; | ||
103 | default: | ||
104 | dprintk("RPC: gss_krb5_seal: ctx->signalg %d not" | ||
105 | " supported\n", ctx->signalg); | ||
106 | goto out_err; | ||
107 | } | ||
108 | if (ctx->sealalg != SEAL_ALG_NONE && ctx->sealalg != SEAL_ALG_DES) { | ||
109 | dprintk("RPC: gss_krb5_seal: ctx->sealalg %d not supported\n", | ||
110 | ctx->sealalg); | ||
111 | goto out_err; | ||
112 | } | ||
113 | |||
114 | if (toktype == KG_TOK_WRAP_MSG) { | ||
115 | blocksize = crypto_tfm_alg_blocksize(ctx->enc); | ||
116 | tmsglen = blocksize + text->len | ||
117 | + gss_krb5_padding(blocksize, blocksize + text->len); | ||
118 | } else { | ||
119 | tmsglen = 0; | ||
120 | } | ||
121 | |||
122 | token->len = g_token_size(&ctx->mech_used, 22 + tmsglen); | ||
123 | |||
124 | ptr = token->data; | ||
125 | g_make_token_header(&ctx->mech_used, 22 + tmsglen, &ptr); | ||
126 | |||
127 | *ptr++ = (unsigned char) ((toktype>>8)&0xff); | ||
128 | *ptr++ = (unsigned char) (toktype&0xff); | ||
129 | |||
130 | /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */ | ||
131 | krb5_hdr = ptr - 2; | ||
132 | msg_start = krb5_hdr + 24; | ||
133 | |||
134 | *(u16 *)(krb5_hdr + 2) = htons(ctx->signalg); | ||
135 | memset(krb5_hdr + 4, 0xff, 4); | ||
136 | if (toktype == KG_TOK_WRAP_MSG) | ||
137 | *(u16 *)(krb5_hdr + 4) = htons(ctx->sealalg); | ||
138 | |||
139 | if (toktype == KG_TOK_WRAP_MSG) { | ||
140 | /* XXX removing support for now */ | ||
141 | goto out_err; | ||
142 | } else { /* Sign only. */ | ||
143 | if (make_checksum(checksum_type, krb5_hdr, 8, text, | ||
144 | &md5cksum)) | ||
145 | goto out_err; | ||
146 | } | ||
147 | |||
148 | switch (ctx->signalg) { | ||
149 | case SGN_ALG_DES_MAC_MD5: | ||
150 | if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, | ||
151 | md5cksum.data, md5cksum.len)) | ||
152 | goto out_err; | ||
153 | memcpy(krb5_hdr + 16, | ||
154 | md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH, | ||
155 | KRB5_CKSUM_LENGTH); | ||
156 | |||
157 | dprintk("RPC: make_seal_token: cksum data: \n"); | ||
158 | print_hexl((u32 *) (krb5_hdr + 16), KRB5_CKSUM_LENGTH, 0); | ||
159 | break; | ||
160 | default: | ||
161 | BUG(); | ||
162 | } | ||
163 | |||
164 | kfree(md5cksum.data); | ||
165 | |||
166 | if ((krb5_make_seq_num(ctx->seq, ctx->initiate ? 0 : 0xff, | ||
167 | ctx->seq_send, krb5_hdr + 16, krb5_hdr + 8))) | ||
168 | goto out_err; | ||
169 | |||
170 | ctx->seq_send++; | ||
171 | |||
172 | return ((ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE); | ||
173 | out_err: | ||
174 | if (md5cksum.data) kfree(md5cksum.data); | ||
175 | return GSS_S_FAILURE; | ||
176 | } | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_seqnum.c b/net/sunrpc/auth_gss/gss_krb5_seqnum.c new file mode 100644 index 000000000000..c53ead39118d --- /dev/null +++ b/net/sunrpc/auth_gss/gss_krb5_seqnum.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_krb5_seqnum.c | ||
3 | * | ||
4 | * Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/util_seqnum.c | ||
5 | * | ||
6 | * Copyright (c) 2000 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Andy Adamson <andros@umich.edu> | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * Copyright 1993 by OpenVision Technologies, Inc. | ||
14 | * | ||
15 | * Permission to use, copy, modify, distribute, and sell this software | ||
16 | * and its documentation for any purpose is hereby granted without fee, | ||
17 | * provided that the above copyright notice appears in all copies and | ||
18 | * that both that copyright notice and this permission notice appear in | ||
19 | * supporting documentation, and that the name of OpenVision not be used | ||
20 | * in advertising or publicity pertaining to distribution of the software | ||
21 | * without specific, written prior permission. OpenVision makes no | ||
22 | * representations about the suitability of this software for any | ||
23 | * purpose. It is provided "as is" without express or implied warranty. | ||
24 | * | ||
25 | * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
26 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
27 | * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
28 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | ||
29 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
30 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
31 | * PERFORMANCE OF THIS SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | #include <linux/types.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/sunrpc/gss_krb5.h> | ||
37 | #include <linux/crypto.h> | ||
38 | |||
39 | #ifdef RPC_DEBUG | ||
40 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
41 | #endif | ||
42 | |||
43 | s32 | ||
44 | krb5_make_seq_num(struct crypto_tfm *key, | ||
45 | int direction, | ||
46 | s32 seqnum, | ||
47 | unsigned char *cksum, unsigned char *buf) | ||
48 | { | ||
49 | unsigned char plain[8]; | ||
50 | |||
51 | plain[0] = (unsigned char) (seqnum & 0xff); | ||
52 | plain[1] = (unsigned char) ((seqnum >> 8) & 0xff); | ||
53 | plain[2] = (unsigned char) ((seqnum >> 16) & 0xff); | ||
54 | plain[3] = (unsigned char) ((seqnum >> 24) & 0xff); | ||
55 | |||
56 | plain[4] = direction; | ||
57 | plain[5] = direction; | ||
58 | plain[6] = direction; | ||
59 | plain[7] = direction; | ||
60 | |||
61 | return krb5_encrypt(key, cksum, plain, buf, 8); | ||
62 | } | ||
63 | |||
64 | s32 | ||
65 | krb5_get_seq_num(struct crypto_tfm *key, | ||
66 | unsigned char *cksum, | ||
67 | unsigned char *buf, | ||
68 | int *direction, s32 * seqnum) | ||
69 | { | ||
70 | s32 code; | ||
71 | unsigned char plain[8]; | ||
72 | |||
73 | dprintk("RPC: krb5_get_seq_num:\n"); | ||
74 | |||
75 | if ((code = krb5_decrypt(key, cksum, buf, plain, 8))) | ||
76 | return code; | ||
77 | |||
78 | if ((plain[4] != plain[5]) || (plain[4] != plain[6]) | ||
79 | || (plain[4] != plain[7])) | ||
80 | return (s32)KG_BAD_SEQ; | ||
81 | |||
82 | *direction = plain[4]; | ||
83 | |||
84 | *seqnum = ((plain[0]) | | ||
85 | (plain[1] << 8) | (plain[2] << 16) | (plain[3] << 24)); | ||
86 | |||
87 | return (0); | ||
88 | } | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c new file mode 100644 index 000000000000..8767fc53183d --- /dev/null +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_krb5_unseal.c | ||
3 | * | ||
4 | * Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/k5unseal.c | ||
5 | * | ||
6 | * Copyright (c) 2000 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Andy Adamson <andros@umich.edu> | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * Copyright 1993 by OpenVision Technologies, Inc. | ||
14 | * | ||
15 | * Permission to use, copy, modify, distribute, and sell this software | ||
16 | * and its documentation for any purpose is hereby granted without fee, | ||
17 | * provided that the above copyright notice appears in all copies and | ||
18 | * that both that copyright notice and this permission notice appear in | ||
19 | * supporting documentation, and that the name of OpenVision not be used | ||
20 | * in advertising or publicity pertaining to distribution of the software | ||
21 | * without specific, written prior permission. OpenVision makes no | ||
22 | * representations about the suitability of this software for any | ||
23 | * purpose. It is provided "as is" without express or implied warranty. | ||
24 | * | ||
25 | * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
26 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
27 | * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
28 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | ||
29 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
30 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
31 | * PERFORMANCE OF THIS SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * Copyright (C) 1998 by the FundsXpress, INC. | ||
36 | * | ||
37 | * All rights reserved. | ||
38 | * | ||
39 | * Export of this software from the United States of America may require | ||
40 | * a specific license from the United States Government. It is the | ||
41 | * responsibility of any person or organization contemplating export to | ||
42 | * obtain such a license before exporting. | ||
43 | * | ||
44 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and | ||
45 | * distribute this software and its documentation for any purpose and | ||
46 | * without fee is hereby granted, provided that the above copyright | ||
47 | * notice appear in all copies and that both that copyright notice and | ||
48 | * this permission notice appear in supporting documentation, and that | ||
49 | * the name of FundsXpress. not be used in advertising or publicity pertaining | ||
50 | * to distribution of the software without specific, written prior | ||
51 | * permission. FundsXpress makes no representations about the suitability of | ||
52 | * this software for any purpose. It is provided "as is" without express | ||
53 | * or implied warranty. | ||
54 | * | ||
55 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | ||
56 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | ||
57 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
58 | */ | ||
59 | |||
60 | #include <linux/types.h> | ||
61 | #include <linux/slab.h> | ||
62 | #include <linux/jiffies.h> | ||
63 | #include <linux/sunrpc/gss_krb5.h> | ||
64 | #include <linux/crypto.h> | ||
65 | |||
66 | #ifdef RPC_DEBUG | ||
67 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
68 | #endif | ||
69 | |||
70 | |||
71 | /* message_buffer is an input if toktype is MIC and an output if it is WRAP: | ||
72 | * If toktype is MIC: read_token is a mic token, and message_buffer is the | ||
73 | * data that the mic was supposedly taken over. | ||
74 | * If toktype is WRAP: read_token is a wrap token, and message_buffer is used | ||
75 | * to return the decrypted data. | ||
76 | */ | ||
77 | |||
78 | /* XXX will need to change prototype and/or just split into a separate function | ||
79 | * when we add privacy (because read_token will be in pages too). */ | ||
80 | u32 | ||
81 | krb5_read_token(struct krb5_ctx *ctx, | ||
82 | struct xdr_netobj *read_token, | ||
83 | struct xdr_buf *message_buffer, | ||
84 | int *qop_state, int toktype) | ||
85 | { | ||
86 | int signalg; | ||
87 | int sealalg; | ||
88 | s32 checksum_type; | ||
89 | struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; | ||
90 | s32 now; | ||
91 | int direction; | ||
92 | s32 seqnum; | ||
93 | unsigned char *ptr = (unsigned char *)read_token->data; | ||
94 | int bodysize; | ||
95 | u32 ret = GSS_S_DEFECTIVE_TOKEN; | ||
96 | |||
97 | dprintk("RPC: krb5_read_token\n"); | ||
98 | |||
99 | if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, | ||
100 | read_token->len)) | ||
101 | goto out; | ||
102 | |||
103 | if ((*ptr++ != ((toktype>>8)&0xff)) || (*ptr++ != (toktype&0xff))) | ||
104 | goto out; | ||
105 | |||
106 | /* XXX sanity-check bodysize?? */ | ||
107 | |||
108 | if (toktype == KG_TOK_WRAP_MSG) { | ||
109 | /* XXX gone */ | ||
110 | goto out; | ||
111 | } | ||
112 | |||
113 | /* get the sign and seal algorithms */ | ||
114 | |||
115 | signalg = ptr[0] + (ptr[1] << 8); | ||
116 | sealalg = ptr[2] + (ptr[3] << 8); | ||
117 | |||
118 | /* Sanity checks */ | ||
119 | |||
120 | if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) | ||
121 | goto out; | ||
122 | |||
123 | if (((toktype != KG_TOK_WRAP_MSG) && (sealalg != 0xffff)) || | ||
124 | ((toktype == KG_TOK_WRAP_MSG) && (sealalg == 0xffff))) | ||
125 | goto out; | ||
126 | |||
127 | /* in the current spec, there is only one valid seal algorithm per | ||
128 | key type, so a simple comparison is ok */ | ||
129 | |||
130 | if ((toktype == KG_TOK_WRAP_MSG) && !(sealalg == ctx->sealalg)) | ||
131 | goto out; | ||
132 | |||
133 | /* there are several mappings of seal algorithms to sign algorithms, | ||
134 | but few enough that we can try them all. */ | ||
135 | |||
136 | if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || | ||
137 | (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || | ||
138 | (ctx->sealalg == SEAL_ALG_DES3KD && | ||
139 | signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) | ||
140 | goto out; | ||
141 | |||
142 | /* compute the checksum of the message */ | ||
143 | |||
144 | /* initialize the the cksum */ | ||
145 | switch (signalg) { | ||
146 | case SGN_ALG_DES_MAC_MD5: | ||
147 | checksum_type = CKSUMTYPE_RSA_MD5; | ||
148 | break; | ||
149 | default: | ||
150 | ret = GSS_S_DEFECTIVE_TOKEN; | ||
151 | goto out; | ||
152 | } | ||
153 | |||
154 | switch (signalg) { | ||
155 | case SGN_ALG_DES_MAC_MD5: | ||
156 | ret = make_checksum(checksum_type, ptr - 2, 8, | ||
157 | message_buffer, &md5cksum); | ||
158 | if (ret) | ||
159 | goto out; | ||
160 | |||
161 | ret = krb5_encrypt(ctx->seq, NULL, md5cksum.data, | ||
162 | md5cksum.data, 16); | ||
163 | if (ret) | ||
164 | goto out; | ||
165 | |||
166 | if (memcmp(md5cksum.data + 8, ptr + 14, 8)) { | ||
167 | ret = GSS_S_BAD_SIG; | ||
168 | goto out; | ||
169 | } | ||
170 | break; | ||
171 | default: | ||
172 | ret = GSS_S_DEFECTIVE_TOKEN; | ||
173 | goto out; | ||
174 | } | ||
175 | |||
176 | /* it got through unscathed. Make sure the context is unexpired */ | ||
177 | |||
178 | if (qop_state) | ||
179 | *qop_state = GSS_C_QOP_DEFAULT; | ||
180 | |||
181 | now = get_seconds(); | ||
182 | |||
183 | ret = GSS_S_CONTEXT_EXPIRED; | ||
184 | if (now > ctx->endtime) | ||
185 | goto out; | ||
186 | |||
187 | /* do sequencing checks */ | ||
188 | |||
189 | ret = GSS_S_BAD_SIG; | ||
190 | if ((ret = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, | ||
191 | &seqnum))) | ||
192 | goto out; | ||
193 | |||
194 | if ((ctx->initiate && direction != 0xff) || | ||
195 | (!ctx->initiate && direction != 0)) | ||
196 | goto out; | ||
197 | |||
198 | ret = GSS_S_COMPLETE; | ||
199 | out: | ||
200 | if (md5cksum.data) kfree(md5cksum.data); | ||
201 | return ret; | ||
202 | } | ||
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c new file mode 100644 index 000000000000..9dfb68377d69 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_mech_switch.c | |||
@@ -0,0 +1,301 @@ | |||
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/module.h> | ||
40 | #include <linux/sunrpc/msg_prot.h> | ||
41 | #include <linux/sunrpc/gss_asn1.h> | ||
42 | #include <linux/sunrpc/auth_gss.h> | ||
43 | #include <linux/sunrpc/svcauth_gss.h> | ||
44 | #include <linux/sunrpc/gss_err.h> | ||
45 | #include <linux/sunrpc/sched.h> | ||
46 | #include <linux/sunrpc/gss_api.h> | ||
47 | #include <linux/sunrpc/clnt.h> | ||
48 | |||
49 | #ifdef RPC_DEBUG | ||
50 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
51 | #endif | ||
52 | |||
53 | static LIST_HEAD(registered_mechs); | ||
54 | static DEFINE_SPINLOCK(registered_mechs_lock); | ||
55 | |||
56 | static void | ||
57 | gss_mech_free(struct gss_api_mech *gm) | ||
58 | { | ||
59 | struct pf_desc *pf; | ||
60 | int i; | ||
61 | |||
62 | for (i = 0; i < gm->gm_pf_num; i++) { | ||
63 | pf = &gm->gm_pfs[i]; | ||
64 | if (pf->auth_domain_name) | ||
65 | kfree(pf->auth_domain_name); | ||
66 | pf->auth_domain_name = NULL; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | static inline char * | ||
71 | make_auth_domain_name(char *name) | ||
72 | { | ||
73 | static char *prefix = "gss/"; | ||
74 | char *new; | ||
75 | |||
76 | new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL); | ||
77 | if (new) { | ||
78 | strcpy(new, prefix); | ||
79 | strcat(new, name); | ||
80 | } | ||
81 | return new; | ||
82 | } | ||
83 | |||
84 | static int | ||
85 | gss_mech_svc_setup(struct gss_api_mech *gm) | ||
86 | { | ||
87 | struct pf_desc *pf; | ||
88 | int i, status; | ||
89 | |||
90 | for (i = 0; i < gm->gm_pf_num; i++) { | ||
91 | pf = &gm->gm_pfs[i]; | ||
92 | pf->auth_domain_name = make_auth_domain_name(pf->name); | ||
93 | status = -ENOMEM; | ||
94 | if (pf->auth_domain_name == NULL) | ||
95 | goto out; | ||
96 | status = svcauth_gss_register_pseudoflavor(pf->pseudoflavor, | ||
97 | pf->auth_domain_name); | ||
98 | if (status) | ||
99 | goto out; | ||
100 | } | ||
101 | return 0; | ||
102 | out: | ||
103 | gss_mech_free(gm); | ||
104 | return status; | ||
105 | } | ||
106 | |||
107 | int | ||
108 | gss_mech_register(struct gss_api_mech *gm) | ||
109 | { | ||
110 | int status; | ||
111 | |||
112 | status = gss_mech_svc_setup(gm); | ||
113 | if (status) | ||
114 | return status; | ||
115 | spin_lock(®istered_mechs_lock); | ||
116 | list_add(&gm->gm_list, ®istered_mechs); | ||
117 | spin_unlock(®istered_mechs_lock); | ||
118 | dprintk("RPC: registered gss mechanism %s\n", gm->gm_name); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | EXPORT_SYMBOL(gss_mech_register); | ||
123 | |||
124 | void | ||
125 | gss_mech_unregister(struct gss_api_mech *gm) | ||
126 | { | ||
127 | spin_lock(®istered_mechs_lock); | ||
128 | list_del(&gm->gm_list); | ||
129 | spin_unlock(®istered_mechs_lock); | ||
130 | dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name); | ||
131 | gss_mech_free(gm); | ||
132 | } | ||
133 | |||
134 | EXPORT_SYMBOL(gss_mech_unregister); | ||
135 | |||
136 | struct gss_api_mech * | ||
137 | gss_mech_get(struct gss_api_mech *gm) | ||
138 | { | ||
139 | __module_get(gm->gm_owner); | ||
140 | return gm; | ||
141 | } | ||
142 | |||
143 | EXPORT_SYMBOL(gss_mech_get); | ||
144 | |||
145 | struct gss_api_mech * | ||
146 | gss_mech_get_by_name(const char *name) | ||
147 | { | ||
148 | struct gss_api_mech *pos, *gm = NULL; | ||
149 | |||
150 | spin_lock(®istered_mechs_lock); | ||
151 | list_for_each_entry(pos, ®istered_mechs, gm_list) { | ||
152 | if (0 == strcmp(name, pos->gm_name)) { | ||
153 | if (try_module_get(pos->gm_owner)) | ||
154 | gm = pos; | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | spin_unlock(®istered_mechs_lock); | ||
159 | return gm; | ||
160 | |||
161 | } | ||
162 | |||
163 | EXPORT_SYMBOL(gss_mech_get_by_name); | ||
164 | |||
165 | static inline int | ||
166 | mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) | ||
167 | { | ||
168 | int i; | ||
169 | |||
170 | for (i = 0; i < gm->gm_pf_num; i++) { | ||
171 | if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) | ||
172 | return 1; | ||
173 | } | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | struct gss_api_mech * | ||
178 | gss_mech_get_by_pseudoflavor(u32 pseudoflavor) | ||
179 | { | ||
180 | struct gss_api_mech *pos, *gm = NULL; | ||
181 | |||
182 | spin_lock(®istered_mechs_lock); | ||
183 | list_for_each_entry(pos, ®istered_mechs, gm_list) { | ||
184 | if (!mech_supports_pseudoflavor(pos, pseudoflavor)) { | ||
185 | module_put(pos->gm_owner); | ||
186 | continue; | ||
187 | } | ||
188 | if (try_module_get(pos->gm_owner)) | ||
189 | gm = pos; | ||
190 | break; | ||
191 | } | ||
192 | spin_unlock(®istered_mechs_lock); | ||
193 | return gm; | ||
194 | } | ||
195 | |||
196 | EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor); | ||
197 | |||
198 | u32 | ||
199 | gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) | ||
200 | { | ||
201 | int i; | ||
202 | |||
203 | for (i = 0; i < gm->gm_pf_num; i++) { | ||
204 | if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) | ||
205 | return gm->gm_pfs[i].service; | ||
206 | } | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | EXPORT_SYMBOL(gss_pseudoflavor_to_service); | ||
211 | |||
212 | char * | ||
213 | gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) | ||
214 | { | ||
215 | int i; | ||
216 | |||
217 | for (i = 0; i < gm->gm_pf_num; i++) { | ||
218 | if (gm->gm_pfs[i].service == service) | ||
219 | return gm->gm_pfs[i].auth_domain_name; | ||
220 | } | ||
221 | return NULL; | ||
222 | } | ||
223 | |||
224 | EXPORT_SYMBOL(gss_service_to_auth_domain_name); | ||
225 | |||
226 | void | ||
227 | gss_mech_put(struct gss_api_mech * gm) | ||
228 | { | ||
229 | module_put(gm->gm_owner); | ||
230 | } | ||
231 | |||
232 | EXPORT_SYMBOL(gss_mech_put); | ||
233 | |||
234 | /* The mech could probably be determined from the token instead, but it's just | ||
235 | * as easy for now to pass it in. */ | ||
236 | int | ||
237 | gss_import_sec_context(const void *input_token, size_t bufsize, | ||
238 | struct gss_api_mech *mech, | ||
239 | struct gss_ctx **ctx_id) | ||
240 | { | ||
241 | if (!(*ctx_id = kmalloc(sizeof(**ctx_id), GFP_KERNEL))) | ||
242 | return GSS_S_FAILURE; | ||
243 | memset(*ctx_id, 0, sizeof(**ctx_id)); | ||
244 | (*ctx_id)->mech_type = gss_mech_get(mech); | ||
245 | |||
246 | return mech->gm_ops | ||
247 | ->gss_import_sec_context(input_token, bufsize, *ctx_id); | ||
248 | } | ||
249 | |||
250 | /* gss_get_mic: compute a mic over message and return mic_token. */ | ||
251 | |||
252 | u32 | ||
253 | gss_get_mic(struct gss_ctx *context_handle, | ||
254 | u32 qop, | ||
255 | struct xdr_buf *message, | ||
256 | struct xdr_netobj *mic_token) | ||
257 | { | ||
258 | return context_handle->mech_type->gm_ops | ||
259 | ->gss_get_mic(context_handle, | ||
260 | qop, | ||
261 | message, | ||
262 | mic_token); | ||
263 | } | ||
264 | |||
265 | /* gss_verify_mic: check whether the provided mic_token verifies message. */ | ||
266 | |||
267 | u32 | ||
268 | gss_verify_mic(struct gss_ctx *context_handle, | ||
269 | struct xdr_buf *message, | ||
270 | struct xdr_netobj *mic_token, | ||
271 | u32 *qstate) | ||
272 | { | ||
273 | return context_handle->mech_type->gm_ops | ||
274 | ->gss_verify_mic(context_handle, | ||
275 | message, | ||
276 | mic_token, | ||
277 | qstate); | ||
278 | } | ||
279 | |||
280 | /* gss_delete_sec_context: free all resources associated with context_handle. | ||
281 | * Note this differs from the RFC 2744-specified prototype in that we don't | ||
282 | * bother returning an output token, since it would never be used anyway. */ | ||
283 | |||
284 | u32 | ||
285 | gss_delete_sec_context(struct gss_ctx **context_handle) | ||
286 | { | ||
287 | dprintk("RPC: gss_delete_sec_context deleting %p\n", | ||
288 | *context_handle); | ||
289 | |||
290 | if (!*context_handle) | ||
291 | return(GSS_S_NO_CONTEXT); | ||
292 | if ((*context_handle)->internal_ctx_id != 0) | ||
293 | (*context_handle)->mech_type->gm_ops | ||
294 | ->gss_delete_sec_context((*context_handle) | ||
295 | ->internal_ctx_id); | ||
296 | if ((*context_handle)->mech_type) | ||
297 | gss_mech_put((*context_handle)->mech_type); | ||
298 | kfree(*context_handle); | ||
299 | *context_handle=NULL; | ||
300 | return GSS_S_COMPLETE; | ||
301 | } | ||
diff --git a/net/sunrpc/auth_gss/gss_spkm3_mech.c b/net/sunrpc/auth_gss/gss_spkm3_mech.c new file mode 100644 index 000000000000..dad05994c3eb --- /dev/null +++ b/net/sunrpc/auth_gss/gss_spkm3_mech.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_spkm3_mech.c | ||
3 | * | ||
4 | * Copyright (c) 2003 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_spkm3.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 | static const void * | ||
53 | simple_get_bytes(const void *p, const void *end, void *res, int len) | ||
54 | { | ||
55 | const void *q = (const void *)((const char *)p + len); | ||
56 | if (unlikely(q > end || q < p)) | ||
57 | return ERR_PTR(-EFAULT); | ||
58 | memcpy(res, p, len); | ||
59 | return q; | ||
60 | } | ||
61 | |||
62 | static const void * | ||
63 | simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res) | ||
64 | { | ||
65 | const void *q; | ||
66 | unsigned int len; | ||
67 | p = simple_get_bytes(p, end, &len, sizeof(len)); | ||
68 | if (IS_ERR(p)) | ||
69 | return p; | ||
70 | res->len = len; | ||
71 | if (len == 0) { | ||
72 | res->data = NULL; | ||
73 | return p; | ||
74 | } | ||
75 | q = (const void *)((const char *)p + len); | ||
76 | if (unlikely(q > end || q < p)) | ||
77 | return ERR_PTR(-EFAULT); | ||
78 | res->data = kmalloc(len, GFP_KERNEL); | ||
79 | if (unlikely(res->data == NULL)) | ||
80 | return ERR_PTR(-ENOMEM); | ||
81 | memcpy(res->data, p, len); | ||
82 | return q; | ||
83 | } | ||
84 | |||
85 | static inline const void * | ||
86 | get_key(const void *p, const void *end, struct crypto_tfm **res, int *resalg) | ||
87 | { | ||
88 | struct xdr_netobj key = { 0 }; | ||
89 | int alg_mode,setkey = 0; | ||
90 | char *alg_name; | ||
91 | |||
92 | p = simple_get_bytes(p, end, resalg, sizeof(*resalg)); | ||
93 | if (IS_ERR(p)) | ||
94 | goto out_err; | ||
95 | p = simple_get_netobj(p, end, &key); | ||
96 | if (IS_ERR(p)) | ||
97 | goto out_err; | ||
98 | |||
99 | switch (*resalg) { | ||
100 | case NID_des_cbc: | ||
101 | alg_name = "des"; | ||
102 | alg_mode = CRYPTO_TFM_MODE_CBC; | ||
103 | setkey = 1; | ||
104 | break; | ||
105 | case NID_md5: | ||
106 | if (key.len == 0) { | ||
107 | dprintk("RPC: SPKM3 get_key: NID_md5 zero Key length\n"); | ||
108 | } | ||
109 | alg_name = "md5"; | ||
110 | alg_mode = 0; | ||
111 | setkey = 0; | ||
112 | break; | ||
113 | default: | ||
114 | dprintk("RPC: SPKM3 get_key: unsupported algorithm %d", *resalg); | ||
115 | goto out_err_free_key; | ||
116 | } | ||
117 | if (!(*res = crypto_alloc_tfm(alg_name, alg_mode))) | ||
118 | goto out_err_free_key; | ||
119 | if (setkey) { | ||
120 | if (crypto_cipher_setkey(*res, key.data, key.len)) | ||
121 | goto out_err_free_tfm; | ||
122 | } | ||
123 | |||
124 | if(key.len > 0) | ||
125 | kfree(key.data); | ||
126 | return p; | ||
127 | |||
128 | out_err_free_tfm: | ||
129 | crypto_free_tfm(*res); | ||
130 | out_err_free_key: | ||
131 | if(key.len > 0) | ||
132 | kfree(key.data); | ||
133 | p = ERR_PTR(-EINVAL); | ||
134 | out_err: | ||
135 | return p; | ||
136 | } | ||
137 | |||
138 | static int | ||
139 | gss_import_sec_context_spkm3(const void *p, size_t len, | ||
140 | struct gss_ctx *ctx_id) | ||
141 | { | ||
142 | const void *end = (const void *)((const char *)p + len); | ||
143 | struct spkm3_ctx *ctx; | ||
144 | |||
145 | if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL))) | ||
146 | goto out_err; | ||
147 | memset(ctx, 0, sizeof(*ctx)); | ||
148 | |||
149 | p = simple_get_netobj(p, end, &ctx->ctx_id); | ||
150 | if (IS_ERR(p)) | ||
151 | goto out_err_free_ctx; | ||
152 | |||
153 | p = simple_get_bytes(p, end, &ctx->qop, sizeof(ctx->qop)); | ||
154 | if (IS_ERR(p)) | ||
155 | goto out_err_free_ctx_id; | ||
156 | |||
157 | p = simple_get_netobj(p, end, &ctx->mech_used); | ||
158 | if (IS_ERR(p)) | ||
159 | goto out_err_free_mech; | ||
160 | |||
161 | p = simple_get_bytes(p, end, &ctx->ret_flags, sizeof(ctx->ret_flags)); | ||
162 | if (IS_ERR(p)) | ||
163 | goto out_err_free_mech; | ||
164 | |||
165 | p = simple_get_bytes(p, end, &ctx->req_flags, sizeof(ctx->req_flags)); | ||
166 | if (IS_ERR(p)) | ||
167 | goto out_err_free_mech; | ||
168 | |||
169 | p = simple_get_netobj(p, end, &ctx->share_key); | ||
170 | if (IS_ERR(p)) | ||
171 | goto out_err_free_s_key; | ||
172 | |||
173 | p = get_key(p, end, &ctx->derived_conf_key, &ctx->conf_alg); | ||
174 | if (IS_ERR(p)) | ||
175 | goto out_err_free_s_key; | ||
176 | |||
177 | p = get_key(p, end, &ctx->derived_integ_key, &ctx->intg_alg); | ||
178 | if (IS_ERR(p)) | ||
179 | goto out_err_free_key1; | ||
180 | |||
181 | p = simple_get_bytes(p, end, &ctx->keyestb_alg, sizeof(ctx->keyestb_alg)); | ||
182 | if (IS_ERR(p)) | ||
183 | goto out_err_free_key2; | ||
184 | |||
185 | p = simple_get_bytes(p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)); | ||
186 | if (IS_ERR(p)) | ||
187 | goto out_err_free_key2; | ||
188 | |||
189 | if (p != end) | ||
190 | goto out_err_free_key2; | ||
191 | |||
192 | ctx_id->internal_ctx_id = ctx; | ||
193 | |||
194 | dprintk("Succesfully imported new spkm context.\n"); | ||
195 | return 0; | ||
196 | |||
197 | out_err_free_key2: | ||
198 | crypto_free_tfm(ctx->derived_integ_key); | ||
199 | out_err_free_key1: | ||
200 | crypto_free_tfm(ctx->derived_conf_key); | ||
201 | out_err_free_s_key: | ||
202 | kfree(ctx->share_key.data); | ||
203 | out_err_free_mech: | ||
204 | kfree(ctx->mech_used.data); | ||
205 | out_err_free_ctx_id: | ||
206 | kfree(ctx->ctx_id.data); | ||
207 | out_err_free_ctx: | ||
208 | kfree(ctx); | ||
209 | out_err: | ||
210 | return PTR_ERR(p); | ||
211 | } | ||
212 | |||
213 | static void | ||
214 | gss_delete_sec_context_spkm3(void *internal_ctx) { | ||
215 | struct spkm3_ctx *sctx = internal_ctx; | ||
216 | |||
217 | if(sctx->derived_integ_key) | ||
218 | crypto_free_tfm(sctx->derived_integ_key); | ||
219 | if(sctx->derived_conf_key) | ||
220 | crypto_free_tfm(sctx->derived_conf_key); | ||
221 | if(sctx->share_key.data) | ||
222 | kfree(sctx->share_key.data); | ||
223 | if(sctx->mech_used.data) | ||
224 | kfree(sctx->mech_used.data); | ||
225 | kfree(sctx); | ||
226 | } | ||
227 | |||
228 | static u32 | ||
229 | gss_verify_mic_spkm3(struct gss_ctx *ctx, | ||
230 | struct xdr_buf *signbuf, | ||
231 | struct xdr_netobj *checksum, | ||
232 | u32 *qstate) { | ||
233 | u32 maj_stat = 0; | ||
234 | int qop_state = 0; | ||
235 | struct spkm3_ctx *sctx = ctx->internal_ctx_id; | ||
236 | |||
237 | dprintk("RPC: gss_verify_mic_spkm3 calling spkm3_read_token\n"); | ||
238 | maj_stat = spkm3_read_token(sctx, checksum, signbuf, &qop_state, | ||
239 | SPKM_MIC_TOK); | ||
240 | |||
241 | if (!maj_stat && qop_state) | ||
242 | *qstate = qop_state; | ||
243 | |||
244 | dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat); | ||
245 | return maj_stat; | ||
246 | } | ||
247 | |||
248 | static u32 | ||
249 | gss_get_mic_spkm3(struct gss_ctx *ctx, | ||
250 | u32 qop, | ||
251 | struct xdr_buf *message_buffer, | ||
252 | struct xdr_netobj *message_token) { | ||
253 | u32 err = 0; | ||
254 | struct spkm3_ctx *sctx = ctx->internal_ctx_id; | ||
255 | |||
256 | dprintk("RPC: gss_get_mic_spkm3\n"); | ||
257 | |||
258 | err = spkm3_make_token(sctx, qop, message_buffer, | ||
259 | message_token, SPKM_MIC_TOK); | ||
260 | return err; | ||
261 | } | ||
262 | |||
263 | static struct gss_api_ops gss_spkm3_ops = { | ||
264 | .gss_import_sec_context = gss_import_sec_context_spkm3, | ||
265 | .gss_get_mic = gss_get_mic_spkm3, | ||
266 | .gss_verify_mic = gss_verify_mic_spkm3, | ||
267 | .gss_delete_sec_context = gss_delete_sec_context_spkm3, | ||
268 | }; | ||
269 | |||
270 | static struct pf_desc gss_spkm3_pfs[] = { | ||
271 | {RPC_AUTH_GSS_SPKM, 0, RPC_GSS_SVC_NONE, "spkm3"}, | ||
272 | {RPC_AUTH_GSS_SPKMI, 0, RPC_GSS_SVC_INTEGRITY, "spkm3i"}, | ||
273 | }; | ||
274 | |||
275 | static struct gss_api_mech gss_spkm3_mech = { | ||
276 | .gm_name = "spkm3", | ||
277 | .gm_owner = THIS_MODULE, | ||
278 | .gm_ops = &gss_spkm3_ops, | ||
279 | .gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs), | ||
280 | .gm_pfs = gss_spkm3_pfs, | ||
281 | }; | ||
282 | |||
283 | static int __init init_spkm3_module(void) | ||
284 | { | ||
285 | int status; | ||
286 | |||
287 | status = gss_mech_register(&gss_spkm3_mech); | ||
288 | if (status) | ||
289 | printk("Failed to register spkm3 gss mechanism!\n"); | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static void __exit cleanup_spkm3_module(void) | ||
294 | { | ||
295 | gss_mech_unregister(&gss_spkm3_mech); | ||
296 | } | ||
297 | |||
298 | MODULE_LICENSE("GPL"); | ||
299 | module_init(init_spkm3_module); | ||
300 | module_exit(cleanup_spkm3_module); | ||
diff --git a/net/sunrpc/auth_gss/gss_spkm3_seal.c b/net/sunrpc/auth_gss/gss_spkm3_seal.c new file mode 100644 index 000000000000..25339868d462 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_spkm3_seal.c | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_spkm3_seal.c | ||
3 | * | ||
4 | * Copyright (c) 2003 The Regents of the University of Michigan. | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Andy Adamson <andros@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/jiffies.h> | ||
39 | #include <linux/sunrpc/gss_spkm3.h> | ||
40 | #include <linux/random.h> | ||
41 | #include <linux/crypto.h> | ||
42 | |||
43 | #ifdef RPC_DEBUG | ||
44 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
45 | #endif | ||
46 | |||
47 | /* | ||
48 | * spkm3_make_token() | ||
49 | * | ||
50 | * Only SPKM_MIC_TOK with md5 intg-alg is supported | ||
51 | */ | ||
52 | |||
53 | u32 | ||
54 | spkm3_make_token(struct spkm3_ctx *ctx, int qop_req, | ||
55 | struct xdr_buf * text, struct xdr_netobj * token, | ||
56 | int toktype) | ||
57 | { | ||
58 | s32 checksum_type; | ||
59 | char tokhdrbuf[25]; | ||
60 | struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; | ||
61 | struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf}; | ||
62 | int tmsglen, tokenlen = 0; | ||
63 | unsigned char *ptr; | ||
64 | s32 now; | ||
65 | int ctxelen = 0, ctxzbit = 0; | ||
66 | int md5elen = 0, md5zbit = 0; | ||
67 | |||
68 | dprintk("RPC: spkm3_make_token\n"); | ||
69 | |||
70 | now = jiffies; | ||
71 | if (qop_req != 0) | ||
72 | goto out_err; | ||
73 | |||
74 | if (ctx->ctx_id.len != 16) { | ||
75 | dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n", | ||
76 | ctx->ctx_id.len); | ||
77 | goto out_err; | ||
78 | } | ||
79 | |||
80 | switch (ctx->intg_alg) { | ||
81 | case NID_md5: | ||
82 | checksum_type = CKSUMTYPE_RSA_MD5; | ||
83 | break; | ||
84 | default: | ||
85 | dprintk("RPC: gss_spkm3_seal: ctx->signalg %d not" | ||
86 | " supported\n", ctx->intg_alg); | ||
87 | goto out_err; | ||
88 | } | ||
89 | /* XXX since we don't support WRAP, perhaps we don't care... */ | ||
90 | if (ctx->conf_alg != NID_cast5_cbc) { | ||
91 | dprintk("RPC: gss_spkm3_seal: ctx->sealalg %d not supported\n", | ||
92 | ctx->conf_alg); | ||
93 | goto out_err; | ||
94 | } | ||
95 | |||
96 | if (toktype == SPKM_MIC_TOK) { | ||
97 | tmsglen = 0; | ||
98 | /* Calculate checksum over the mic-header */ | ||
99 | asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit); | ||
100 | spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data, | ||
101 | ctxelen, ctxzbit); | ||
102 | |||
103 | if (make_checksum(checksum_type, mic_hdr.data, mic_hdr.len, | ||
104 | text, &md5cksum)) | ||
105 | goto out_err; | ||
106 | |||
107 | asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit); | ||
108 | tokenlen = 10 + ctxelen + 1 + 2 + md5elen + 1; | ||
109 | |||
110 | /* Create token header using generic routines */ | ||
111 | token->len = g_token_size(&ctx->mech_used, tokenlen + tmsglen); | ||
112 | |||
113 | ptr = token->data; | ||
114 | g_make_token_header(&ctx->mech_used, tokenlen + tmsglen, &ptr); | ||
115 | |||
116 | spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit); | ||
117 | } else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */ | ||
118 | dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK not supported\n"); | ||
119 | goto out_err; | ||
120 | } | ||
121 | kfree(md5cksum.data); | ||
122 | |||
123 | /* XXX need to implement sequence numbers, and ctx->expired */ | ||
124 | |||
125 | return GSS_S_COMPLETE; | ||
126 | out_err: | ||
127 | if (md5cksum.data) | ||
128 | kfree(md5cksum.data); | ||
129 | token->data = NULL; | ||
130 | token->len = 0; | ||
131 | return GSS_S_FAILURE; | ||
132 | } | ||
diff --git a/net/sunrpc/auth_gss/gss_spkm3_token.c b/net/sunrpc/auth_gss/gss_spkm3_token.c new file mode 100644 index 000000000000..46c08a0710f6 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_spkm3_token.c | |||
@@ -0,0 +1,266 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_spkm3_token.c | ||
3 | * | ||
4 | * Copyright (c) 2003 The Regents of the University of Michigan. | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Andy Adamson <andros@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/jiffies.h> | ||
39 | #include <linux/sunrpc/gss_spkm3.h> | ||
40 | #include <linux/random.h> | ||
41 | #include <linux/crypto.h> | ||
42 | |||
43 | #ifdef RPC_DEBUG | ||
44 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
45 | #endif | ||
46 | |||
47 | /* | ||
48 | * asn1_bitstring_len() | ||
49 | * | ||
50 | * calculate the asn1 bitstring length of the xdr_netobject | ||
51 | */ | ||
52 | void | ||
53 | asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits) | ||
54 | { | ||
55 | int i, zbit = 0,elen = in->len; | ||
56 | char *ptr; | ||
57 | |||
58 | ptr = &in->data[in->len -1]; | ||
59 | |||
60 | /* count trailing 0's */ | ||
61 | for(i = in->len; i > 0; i--) { | ||
62 | if (*ptr == 0) { | ||
63 | ptr--; | ||
64 | elen--; | ||
65 | } else | ||
66 | break; | ||
67 | } | ||
68 | |||
69 | /* count number of 0 bits in final octet */ | ||
70 | ptr = &in->data[elen - 1]; | ||
71 | for(i = 0; i < 8; i++) { | ||
72 | short mask = 0x01; | ||
73 | |||
74 | if (!((mask << i) & *ptr)) | ||
75 | zbit++; | ||
76 | else | ||
77 | break; | ||
78 | } | ||
79 | *enclen = elen; | ||
80 | *zerobits = zbit; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * decode_asn1_bitstring() | ||
85 | * | ||
86 | * decode a bitstring into a buffer of the expected length. | ||
87 | * enclen = bit string length | ||
88 | * explen = expected length (define in rfc) | ||
89 | */ | ||
90 | int | ||
91 | decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen) | ||
92 | { | ||
93 | if (!(out->data = kmalloc(explen,GFP_KERNEL))) | ||
94 | return 0; | ||
95 | out->len = explen; | ||
96 | memset(out->data, 0, explen); | ||
97 | memcpy(out->data, in, enclen); | ||
98 | return 1; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * SPKMInnerContextToken choice SPKM_MIC asn1 token layout | ||
103 | * | ||
104 | * contextid is always 16 bytes plain data. max asn1 bitstring len = 17. | ||
105 | * | ||
106 | * tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum) | ||
107 | * | ||
108 | * pos value | ||
109 | * ---------- | ||
110 | * [0] a4 SPKM-MIC tag | ||
111 | * [1] ?? innertoken length (max 44) | ||
112 | * | ||
113 | * | ||
114 | * tok_hdr piece of checksum data starts here | ||
115 | * | ||
116 | * the maximum mic-header len = 9 + 17 = 26 | ||
117 | * mic-header | ||
118 | * ---------- | ||
119 | * [2] 30 SEQUENCE tag | ||
120 | * [3] ?? mic-header length: (max 23) = TokenID + ContextID | ||
121 | * | ||
122 | * TokenID - all fields constant and can be hardcoded | ||
123 | * ------- | ||
124 | * [4] 02 Type 2 | ||
125 | * [5] 02 Length 2 | ||
126 | * [6][7] 01 01 TokenID (SPKM_MIC_TOK) | ||
127 | * | ||
128 | * ContextID - encoded length not constant, calculated | ||
129 | * --------- | ||
130 | * [8] 03 Type 3 | ||
131 | * [9] ?? encoded length | ||
132 | * [10] ?? ctxzbit | ||
133 | * [11] contextid | ||
134 | * | ||
135 | * mic_header piece of checksum data ends here. | ||
136 | * | ||
137 | * int-cksum - encoded length not constant, calculated | ||
138 | * --------- | ||
139 | * [??] 03 Type 3 | ||
140 | * [??] ?? encoded length | ||
141 | * [??] ?? md5zbit | ||
142 | * [??] int-cksum (NID_md5 = 16) | ||
143 | * | ||
144 | * maximum SPKM-MIC innercontext token length = | ||
145 | * 10 + encoded contextid_size(17 max) + 2 + encoded | ||
146 | * cksum_size (17 maxfor NID_md5) = 46 | ||
147 | */ | ||
148 | |||
149 | /* | ||
150 | * spkm3_mic_header() | ||
151 | * | ||
152 | * Prepare the SPKM_MIC_TOK mic-header for check-sum calculation | ||
153 | * elen: 16 byte context id asn1 bitstring encoded length | ||
154 | */ | ||
155 | void | ||
156 | spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit) | ||
157 | { | ||
158 | char *hptr = *hdrbuf; | ||
159 | char *top = *hdrbuf; | ||
160 | |||
161 | *(u8 *)hptr++ = 0x30; | ||
162 | *(u8 *)hptr++ = elen + 7; /* on the wire header length */ | ||
163 | |||
164 | /* tokenid */ | ||
165 | *(u8 *)hptr++ = 0x02; | ||
166 | *(u8 *)hptr++ = 0x02; | ||
167 | *(u8 *)hptr++ = 0x01; | ||
168 | *(u8 *)hptr++ = 0x01; | ||
169 | |||
170 | /* coniextid */ | ||
171 | *(u8 *)hptr++ = 0x03; | ||
172 | *(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */ | ||
173 | *(u8 *)hptr++ = zbit; | ||
174 | memcpy(hptr, ctxdata, elen); | ||
175 | hptr += elen; | ||
176 | *hdrlen = hptr - top; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * spkm3_mic_innercontext_token() | ||
181 | * | ||
182 | * *tokp points to the beginning of the SPKM_MIC token described | ||
183 | * in rfc 2025, section 3.2.1: | ||
184 | * | ||
185 | */ | ||
186 | void | ||
187 | spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit) | ||
188 | { | ||
189 | unsigned char *ict = *tokp; | ||
190 | |||
191 | *(u8 *)ict++ = 0xa4; | ||
192 | *(u8 *)ict++ = toklen - 2; | ||
193 | memcpy(ict, mic_hdr->data, mic_hdr->len); | ||
194 | ict += mic_hdr->len; | ||
195 | |||
196 | *(u8 *)ict++ = 0x03; | ||
197 | *(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */ | ||
198 | *(u8 *)ict++ = md5zbit; | ||
199 | memcpy(ict, md5cksum->data, md5elen); | ||
200 | } | ||
201 | |||
202 | u32 | ||
203 | spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum) | ||
204 | { | ||
205 | struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL}; | ||
206 | unsigned char *ptr = *tokp; | ||
207 | int ctxelen; | ||
208 | u32 ret = GSS_S_DEFECTIVE_TOKEN; | ||
209 | |||
210 | /* spkm3 innercontext token preamble */ | ||
211 | if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) { | ||
212 | dprintk("RPC: BAD SPKM ictoken preamble\n"); | ||
213 | goto out; | ||
214 | } | ||
215 | |||
216 | *mic_hdrlen = ptr[3]; | ||
217 | |||
218 | /* token type */ | ||
219 | if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) { | ||
220 | dprintk("RPC: BAD asn1 SPKM3 token type\n"); | ||
221 | goto out; | ||
222 | } | ||
223 | |||
224 | /* only support SPKM_MIC_TOK */ | ||
225 | if((ptr[6] != 0x01) || (ptr[7] != 0x01)) { | ||
226 | dprintk("RPC: ERROR unsupported SPKM3 token \n"); | ||
227 | goto out; | ||
228 | } | ||
229 | |||
230 | /* contextid */ | ||
231 | if (ptr[8] != 0x03) { | ||
232 | dprintk("RPC: BAD SPKM3 asn1 context-id type\n"); | ||
233 | goto out; | ||
234 | } | ||
235 | |||
236 | ctxelen = ptr[9]; | ||
237 | if (ctxelen > 17) { /* length includes asn1 zbit octet */ | ||
238 | dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen); | ||
239 | goto out; | ||
240 | } | ||
241 | |||
242 | /* ignore ptr[10] */ | ||
243 | |||
244 | if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16)) | ||
245 | goto out; | ||
246 | |||
247 | /* | ||
248 | * in the current implementation: the optional int-alg is not present | ||
249 | * so the default int-alg (md5) is used the optional snd-seq field is | ||
250 | * also not present | ||
251 | */ | ||
252 | |||
253 | if (*mic_hdrlen != 6 + ctxelen) { | ||
254 | dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only support default int-alg (should be absent) and do not support snd-seq\n", *mic_hdrlen); | ||
255 | goto out; | ||
256 | } | ||
257 | /* checksum */ | ||
258 | *cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */ | ||
259 | |||
260 | ret = GSS_S_COMPLETE; | ||
261 | out: | ||
262 | if (spkm3_ctx_id.data) | ||
263 | kfree(spkm3_ctx_id.data); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
diff --git a/net/sunrpc/auth_gss/gss_spkm3_unseal.c b/net/sunrpc/auth_gss/gss_spkm3_unseal.c new file mode 100644 index 000000000000..65ce81bf0bc4 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_spkm3_unseal.c | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/gss_spkm3_unseal.c | ||
3 | * | ||
4 | * Copyright (c) 2003 The Regents of the University of Michigan. | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Andy Adamson <andros@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/jiffies.h> | ||
39 | #include <linux/sunrpc/gss_spkm3.h> | ||
40 | #include <linux/crypto.h> | ||
41 | |||
42 | #ifdef RPC_DEBUG | ||
43 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
44 | #endif | ||
45 | |||
46 | /* | ||
47 | * spkm3_read_token() | ||
48 | * | ||
49 | * only SPKM_MIC_TOK with md5 intg-alg is supported | ||
50 | */ | ||
51 | u32 | ||
52 | spkm3_read_token(struct spkm3_ctx *ctx, | ||
53 | struct xdr_netobj *read_token, /* checksum */ | ||
54 | struct xdr_buf *message_buffer, /* signbuf */ | ||
55 | int *qop_state, int toktype) | ||
56 | { | ||
57 | s32 code; | ||
58 | struct xdr_netobj wire_cksum = {.len =0, .data = NULL}; | ||
59 | struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; | ||
60 | unsigned char *ptr = (unsigned char *)read_token->data; | ||
61 | unsigned char *cksum; | ||
62 | int bodysize, md5elen; | ||
63 | int mic_hdrlen; | ||
64 | u32 ret = GSS_S_DEFECTIVE_TOKEN; | ||
65 | |||
66 | dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len); | ||
67 | |||
68 | if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, | ||
69 | &bodysize, &ptr, read_token->len)) | ||
70 | goto out; | ||
71 | |||
72 | /* decode the token */ | ||
73 | |||
74 | if (toktype == SPKM_MIC_TOK) { | ||
75 | |||
76 | if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum))) | ||
77 | goto out; | ||
78 | |||
79 | if (*cksum++ != 0x03) { | ||
80 | dprintk("RPC: spkm3_read_token BAD checksum type\n"); | ||
81 | goto out; | ||
82 | } | ||
83 | md5elen = *cksum++; | ||
84 | cksum++; /* move past the zbit */ | ||
85 | |||
86 | if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16)) | ||
87 | goto out; | ||
88 | |||
89 | /* HARD CODED FOR MD5 */ | ||
90 | |||
91 | /* compute the checksum of the message. | ||
92 | * ptr + 2 = start of header piece of checksum | ||
93 | * mic_hdrlen + 2 = length of header piece of checksum | ||
94 | */ | ||
95 | ret = GSS_S_DEFECTIVE_TOKEN; | ||
96 | code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2, | ||
97 | mic_hdrlen + 2, | ||
98 | message_buffer, &md5cksum); | ||
99 | |||
100 | if (code) | ||
101 | goto out; | ||
102 | |||
103 | dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n", | ||
104 | wire_cksum.len); | ||
105 | dprintk(" md5cksum.data\n"); | ||
106 | print_hexl((u32 *) md5cksum.data, 16, 0); | ||
107 | dprintk(" cksum.data:\n"); | ||
108 | print_hexl((u32 *) wire_cksum.data, wire_cksum.len, 0); | ||
109 | |||
110 | ret = GSS_S_BAD_SIG; | ||
111 | code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len); | ||
112 | if (code) | ||
113 | goto out; | ||
114 | |||
115 | } else { | ||
116 | dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | /* XXX: need to add expiration and sequencing */ | ||
121 | ret = GSS_S_COMPLETE; | ||
122 | out: | ||
123 | if (md5cksum.data) | ||
124 | kfree(md5cksum.data); | ||
125 | if (wire_cksum.data) | ||
126 | kfree(wire_cksum.data); | ||
127 | return ret; | ||
128 | } | ||
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c new file mode 100644 index 000000000000..5c8fe3bfc494 --- /dev/null +++ b/net/sunrpc/auth_gss/svcauth_gss.c | |||
@@ -0,0 +1,1080 @@ | |||
1 | /* | ||
2 | * Neil Brown <neilb@cse.unsw.edu.au> | ||
3 | * J. Bruce Fields <bfields@umich.edu> | ||
4 | * Andy Adamson <andros@umich.edu> | ||
5 | * Dug Song <dugsong@monkey.org> | ||
6 | * | ||
7 | * RPCSEC_GSS server authentication. | ||
8 | * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078 | ||
9 | * (gssapi) | ||
10 | * | ||
11 | * The RPCSEC_GSS involves three stages: | ||
12 | * 1/ context creation | ||
13 | * 2/ data exchange | ||
14 | * 3/ context destruction | ||
15 | * | ||
16 | * Context creation is handled largely by upcalls to user-space. | ||
17 | * In particular, GSS_Accept_sec_context is handled by an upcall | ||
18 | * Data exchange is handled entirely within the kernel | ||
19 | * In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel. | ||
20 | * Context destruction is handled in-kernel | ||
21 | * GSS_Delete_sec_context is in-kernel | ||
22 | * | ||
23 | * Context creation is initiated by a RPCSEC_GSS_INIT request arriving. | ||
24 | * The context handle and gss_token are used as a key into the rpcsec_init cache. | ||
25 | * The content of this cache includes some of the outputs of GSS_Accept_sec_context, | ||
26 | * being major_status, minor_status, context_handle, reply_token. | ||
27 | * These are sent back to the client. | ||
28 | * Sequence window management is handled by the kernel. The window size if currently | ||
29 | * a compile time constant. | ||
30 | * | ||
31 | * When user-space is happy that a context is established, it places an entry | ||
32 | * in the rpcsec_context cache. The key for this cache is the context_handle. | ||
33 | * The content includes: | ||
34 | * uid/gidlist - for determining access rights | ||
35 | * mechanism type | ||
36 | * mechanism specific information, such as a key | ||
37 | * | ||
38 | */ | ||
39 | |||
40 | #include <linux/types.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <linux/pagemap.h> | ||
43 | |||
44 | #include <linux/sunrpc/auth_gss.h> | ||
45 | #include <linux/sunrpc/svcauth.h> | ||
46 | #include <linux/sunrpc/gss_err.h> | ||
47 | #include <linux/sunrpc/svcauth.h> | ||
48 | #include <linux/sunrpc/svcauth_gss.h> | ||
49 | #include <linux/sunrpc/cache.h> | ||
50 | |||
51 | #ifdef RPC_DEBUG | ||
52 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
53 | #endif | ||
54 | |||
55 | /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests | ||
56 | * into replies. | ||
57 | * | ||
58 | * Key is context handle (\x if empty) and gss_token. | ||
59 | * Content is major_status minor_status (integers) context_handle, reply_token. | ||
60 | * | ||
61 | */ | ||
62 | |||
63 | static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) | ||
64 | { | ||
65 | return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); | ||
66 | } | ||
67 | |||
68 | #define RSI_HASHBITS 6 | ||
69 | #define RSI_HASHMAX (1<<RSI_HASHBITS) | ||
70 | #define RSI_HASHMASK (RSI_HASHMAX-1) | ||
71 | |||
72 | struct rsi { | ||
73 | struct cache_head h; | ||
74 | struct xdr_netobj in_handle, in_token; | ||
75 | struct xdr_netobj out_handle, out_token; | ||
76 | int major_status, minor_status; | ||
77 | }; | ||
78 | |||
79 | static struct cache_head *rsi_table[RSI_HASHMAX]; | ||
80 | static struct cache_detail rsi_cache; | ||
81 | static struct rsi *rsi_lookup(struct rsi *item, int set); | ||
82 | |||
83 | static void rsi_free(struct rsi *rsii) | ||
84 | { | ||
85 | kfree(rsii->in_handle.data); | ||
86 | kfree(rsii->in_token.data); | ||
87 | kfree(rsii->out_handle.data); | ||
88 | kfree(rsii->out_token.data); | ||
89 | } | ||
90 | |||
91 | static void rsi_put(struct cache_head *item, struct cache_detail *cd) | ||
92 | { | ||
93 | struct rsi *rsii = container_of(item, struct rsi, h); | ||
94 | if (cache_put(item, cd)) { | ||
95 | rsi_free(rsii); | ||
96 | kfree(rsii); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static inline int rsi_hash(struct rsi *item) | ||
101 | { | ||
102 | return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) | ||
103 | ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); | ||
104 | } | ||
105 | |||
106 | static inline int rsi_match(struct rsi *item, struct rsi *tmp) | ||
107 | { | ||
108 | return netobj_equal(&item->in_handle, &tmp->in_handle) | ||
109 | && netobj_equal(&item->in_token, &tmp->in_token); | ||
110 | } | ||
111 | |||
112 | static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) | ||
113 | { | ||
114 | dst->len = len; | ||
115 | dst->data = (len ? kmalloc(len, GFP_KERNEL) : NULL); | ||
116 | if (dst->data) | ||
117 | memcpy(dst->data, src, len); | ||
118 | if (len && !dst->data) | ||
119 | return -ENOMEM; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) | ||
124 | { | ||
125 | return dup_to_netobj(dst, src->data, src->len); | ||
126 | } | ||
127 | |||
128 | static inline void rsi_init(struct rsi *new, struct rsi *item) | ||
129 | { | ||
130 | new->out_handle.data = NULL; | ||
131 | new->out_handle.len = 0; | ||
132 | new->out_token.data = NULL; | ||
133 | new->out_token.len = 0; | ||
134 | new->in_handle.len = item->in_handle.len; | ||
135 | item->in_handle.len = 0; | ||
136 | new->in_token.len = item->in_token.len; | ||
137 | item->in_token.len = 0; | ||
138 | new->in_handle.data = item->in_handle.data; | ||
139 | item->in_handle.data = NULL; | ||
140 | new->in_token.data = item->in_token.data; | ||
141 | item->in_token.data = NULL; | ||
142 | } | ||
143 | |||
144 | static inline void rsi_update(struct rsi *new, struct rsi *item) | ||
145 | { | ||
146 | BUG_ON(new->out_handle.data || new->out_token.data); | ||
147 | new->out_handle.len = item->out_handle.len; | ||
148 | item->out_handle.len = 0; | ||
149 | new->out_token.len = item->out_token.len; | ||
150 | item->out_token.len = 0; | ||
151 | new->out_handle.data = item->out_handle.data; | ||
152 | item->out_handle.data = NULL; | ||
153 | new->out_token.data = item->out_token.data; | ||
154 | item->out_token.data = NULL; | ||
155 | |||
156 | new->major_status = item->major_status; | ||
157 | new->minor_status = item->minor_status; | ||
158 | } | ||
159 | |||
160 | static void rsi_request(struct cache_detail *cd, | ||
161 | struct cache_head *h, | ||
162 | char **bpp, int *blen) | ||
163 | { | ||
164 | struct rsi *rsii = container_of(h, struct rsi, h); | ||
165 | |||
166 | qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); | ||
167 | qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); | ||
168 | (*bpp)[-1] = '\n'; | ||
169 | } | ||
170 | |||
171 | |||
172 | static int rsi_parse(struct cache_detail *cd, | ||
173 | char *mesg, int mlen) | ||
174 | { | ||
175 | /* context token expiry major minor context token */ | ||
176 | char *buf = mesg; | ||
177 | char *ep; | ||
178 | int len; | ||
179 | struct rsi rsii, *rsip = NULL; | ||
180 | time_t expiry; | ||
181 | int status = -EINVAL; | ||
182 | |||
183 | memset(&rsii, 0, sizeof(rsii)); | ||
184 | /* handle */ | ||
185 | len = qword_get(&mesg, buf, mlen); | ||
186 | if (len < 0) | ||
187 | goto out; | ||
188 | status = -ENOMEM; | ||
189 | if (dup_to_netobj(&rsii.in_handle, buf, len)) | ||
190 | goto out; | ||
191 | |||
192 | /* token */ | ||
193 | len = qword_get(&mesg, buf, mlen); | ||
194 | status = -EINVAL; | ||
195 | if (len < 0) | ||
196 | goto out; | ||
197 | status = -ENOMEM; | ||
198 | if (dup_to_netobj(&rsii.in_token, buf, len)) | ||
199 | goto out; | ||
200 | |||
201 | rsii.h.flags = 0; | ||
202 | /* expiry */ | ||
203 | expiry = get_expiry(&mesg); | ||
204 | status = -EINVAL; | ||
205 | if (expiry == 0) | ||
206 | goto out; | ||
207 | |||
208 | /* major/minor */ | ||
209 | len = qword_get(&mesg, buf, mlen); | ||
210 | if (len < 0) | ||
211 | goto out; | ||
212 | if (len == 0) { | ||
213 | goto out; | ||
214 | } else { | ||
215 | rsii.major_status = simple_strtoul(buf, &ep, 10); | ||
216 | if (*ep) | ||
217 | goto out; | ||
218 | len = qword_get(&mesg, buf, mlen); | ||
219 | if (len <= 0) | ||
220 | goto out; | ||
221 | rsii.minor_status = simple_strtoul(buf, &ep, 10); | ||
222 | if (*ep) | ||
223 | goto out; | ||
224 | |||
225 | /* out_handle */ | ||
226 | len = qword_get(&mesg, buf, mlen); | ||
227 | if (len < 0) | ||
228 | goto out; | ||
229 | status = -ENOMEM; | ||
230 | if (dup_to_netobj(&rsii.out_handle, buf, len)) | ||
231 | goto out; | ||
232 | |||
233 | /* out_token */ | ||
234 | len = qword_get(&mesg, buf, mlen); | ||
235 | status = -EINVAL; | ||
236 | if (len < 0) | ||
237 | goto out; | ||
238 | status = -ENOMEM; | ||
239 | if (dup_to_netobj(&rsii.out_token, buf, len)) | ||
240 | goto out; | ||
241 | } | ||
242 | rsii.h.expiry_time = expiry; | ||
243 | rsip = rsi_lookup(&rsii, 1); | ||
244 | status = 0; | ||
245 | out: | ||
246 | rsi_free(&rsii); | ||
247 | if (rsip) | ||
248 | rsi_put(&rsip->h, &rsi_cache); | ||
249 | return status; | ||
250 | } | ||
251 | |||
252 | static struct cache_detail rsi_cache = { | ||
253 | .hash_size = RSI_HASHMAX, | ||
254 | .hash_table = rsi_table, | ||
255 | .name = "auth.rpcsec.init", | ||
256 | .cache_put = rsi_put, | ||
257 | .cache_request = rsi_request, | ||
258 | .cache_parse = rsi_parse, | ||
259 | }; | ||
260 | |||
261 | static DefineSimpleCacheLookup(rsi, 0) | ||
262 | |||
263 | /* | ||
264 | * The rpcsec_context cache is used to store a context that is | ||
265 | * used in data exchange. | ||
266 | * The key is a context handle. The content is: | ||
267 | * uid, gidlist, mechanism, service-set, mech-specific-data | ||
268 | */ | ||
269 | |||
270 | #define RSC_HASHBITS 10 | ||
271 | #define RSC_HASHMAX (1<<RSC_HASHBITS) | ||
272 | #define RSC_HASHMASK (RSC_HASHMAX-1) | ||
273 | |||
274 | #define GSS_SEQ_WIN 128 | ||
275 | |||
276 | struct gss_svc_seq_data { | ||
277 | /* highest seq number seen so far: */ | ||
278 | int sd_max; | ||
279 | /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of | ||
280 | * sd_win is nonzero iff sequence number i has been seen already: */ | ||
281 | unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; | ||
282 | spinlock_t sd_lock; | ||
283 | }; | ||
284 | |||
285 | struct rsc { | ||
286 | struct cache_head h; | ||
287 | struct xdr_netobj handle; | ||
288 | struct svc_cred cred; | ||
289 | struct gss_svc_seq_data seqdata; | ||
290 | struct gss_ctx *mechctx; | ||
291 | }; | ||
292 | |||
293 | static struct cache_head *rsc_table[RSC_HASHMAX]; | ||
294 | static struct cache_detail rsc_cache; | ||
295 | static struct rsc *rsc_lookup(struct rsc *item, int set); | ||
296 | |||
297 | static void rsc_free(struct rsc *rsci) | ||
298 | { | ||
299 | kfree(rsci->handle.data); | ||
300 | if (rsci->mechctx) | ||
301 | gss_delete_sec_context(&rsci->mechctx); | ||
302 | if (rsci->cred.cr_group_info) | ||
303 | put_group_info(rsci->cred.cr_group_info); | ||
304 | } | ||
305 | |||
306 | static void rsc_put(struct cache_head *item, struct cache_detail *cd) | ||
307 | { | ||
308 | struct rsc *rsci = container_of(item, struct rsc, h); | ||
309 | |||
310 | if (cache_put(item, cd)) { | ||
311 | rsc_free(rsci); | ||
312 | kfree(rsci); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | static inline int | ||
317 | rsc_hash(struct rsc *rsci) | ||
318 | { | ||
319 | return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); | ||
320 | } | ||
321 | |||
322 | static inline int | ||
323 | rsc_match(struct rsc *new, struct rsc *tmp) | ||
324 | { | ||
325 | return netobj_equal(&new->handle, &tmp->handle); | ||
326 | } | ||
327 | |||
328 | static inline void | ||
329 | rsc_init(struct rsc *new, struct rsc *tmp) | ||
330 | { | ||
331 | new->handle.len = tmp->handle.len; | ||
332 | tmp->handle.len = 0; | ||
333 | new->handle.data = tmp->handle.data; | ||
334 | tmp->handle.data = NULL; | ||
335 | new->mechctx = NULL; | ||
336 | new->cred.cr_group_info = NULL; | ||
337 | } | ||
338 | |||
339 | static inline void | ||
340 | rsc_update(struct rsc *new, struct rsc *tmp) | ||
341 | { | ||
342 | new->mechctx = tmp->mechctx; | ||
343 | tmp->mechctx = NULL; | ||
344 | memset(&new->seqdata, 0, sizeof(new->seqdata)); | ||
345 | spin_lock_init(&new->seqdata.sd_lock); | ||
346 | new->cred = tmp->cred; | ||
347 | tmp->cred.cr_group_info = NULL; | ||
348 | } | ||
349 | |||
350 | static int rsc_parse(struct cache_detail *cd, | ||
351 | char *mesg, int mlen) | ||
352 | { | ||
353 | /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ | ||
354 | char *buf = mesg; | ||
355 | int len, rv; | ||
356 | struct rsc rsci, *rscp = NULL; | ||
357 | time_t expiry; | ||
358 | int status = -EINVAL; | ||
359 | |||
360 | memset(&rsci, 0, sizeof(rsci)); | ||
361 | /* context handle */ | ||
362 | len = qword_get(&mesg, buf, mlen); | ||
363 | if (len < 0) goto out; | ||
364 | status = -ENOMEM; | ||
365 | if (dup_to_netobj(&rsci.handle, buf, len)) | ||
366 | goto out; | ||
367 | |||
368 | rsci.h.flags = 0; | ||
369 | /* expiry */ | ||
370 | expiry = get_expiry(&mesg); | ||
371 | status = -EINVAL; | ||
372 | if (expiry == 0) | ||
373 | goto out; | ||
374 | |||
375 | /* uid, or NEGATIVE */ | ||
376 | rv = get_int(&mesg, &rsci.cred.cr_uid); | ||
377 | if (rv == -EINVAL) | ||
378 | goto out; | ||
379 | if (rv == -ENOENT) | ||
380 | set_bit(CACHE_NEGATIVE, &rsci.h.flags); | ||
381 | else { | ||
382 | int N, i; | ||
383 | struct gss_api_mech *gm; | ||
384 | |||
385 | /* gid */ | ||
386 | if (get_int(&mesg, &rsci.cred.cr_gid)) | ||
387 | goto out; | ||
388 | |||
389 | /* number of additional gid's */ | ||
390 | if (get_int(&mesg, &N)) | ||
391 | goto out; | ||
392 | status = -ENOMEM; | ||
393 | rsci.cred.cr_group_info = groups_alloc(N); | ||
394 | if (rsci.cred.cr_group_info == NULL) | ||
395 | goto out; | ||
396 | |||
397 | /* gid's */ | ||
398 | status = -EINVAL; | ||
399 | for (i=0; i<N; i++) { | ||
400 | gid_t gid; | ||
401 | if (get_int(&mesg, &gid)) | ||
402 | goto out; | ||
403 | GROUP_AT(rsci.cred.cr_group_info, i) = gid; | ||
404 | } | ||
405 | |||
406 | /* mech name */ | ||
407 | len = qword_get(&mesg, buf, mlen); | ||
408 | if (len < 0) | ||
409 | goto out; | ||
410 | gm = gss_mech_get_by_name(buf); | ||
411 | status = -EOPNOTSUPP; | ||
412 | if (!gm) | ||
413 | goto out; | ||
414 | |||
415 | status = -EINVAL; | ||
416 | /* mech-specific data: */ | ||
417 | len = qword_get(&mesg, buf, mlen); | ||
418 | if (len < 0) { | ||
419 | gss_mech_put(gm); | ||
420 | goto out; | ||
421 | } | ||
422 | if (gss_import_sec_context(buf, len, gm, &rsci.mechctx)) { | ||
423 | gss_mech_put(gm); | ||
424 | goto out; | ||
425 | } | ||
426 | gss_mech_put(gm); | ||
427 | } | ||
428 | rsci.h.expiry_time = expiry; | ||
429 | rscp = rsc_lookup(&rsci, 1); | ||
430 | status = 0; | ||
431 | out: | ||
432 | rsc_free(&rsci); | ||
433 | if (rscp) | ||
434 | rsc_put(&rscp->h, &rsc_cache); | ||
435 | return status; | ||
436 | } | ||
437 | |||
438 | static struct cache_detail rsc_cache = { | ||
439 | .hash_size = RSC_HASHMAX, | ||
440 | .hash_table = rsc_table, | ||
441 | .name = "auth.rpcsec.context", | ||
442 | .cache_put = rsc_put, | ||
443 | .cache_parse = rsc_parse, | ||
444 | }; | ||
445 | |||
446 | static DefineSimpleCacheLookup(rsc, 0); | ||
447 | |||
448 | static struct rsc * | ||
449 | gss_svc_searchbyctx(struct xdr_netobj *handle) | ||
450 | { | ||
451 | struct rsc rsci; | ||
452 | struct rsc *found; | ||
453 | |||
454 | memset(&rsci, 0, sizeof(rsci)); | ||
455 | if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) | ||
456 | return NULL; | ||
457 | found = rsc_lookup(&rsci, 0); | ||
458 | rsc_free(&rsci); | ||
459 | if (!found) | ||
460 | return NULL; | ||
461 | if (cache_check(&rsc_cache, &found->h, NULL)) | ||
462 | return NULL; | ||
463 | return found; | ||
464 | } | ||
465 | |||
466 | /* Implements sequence number algorithm as specified in RFC 2203. */ | ||
467 | static int | ||
468 | gss_check_seq_num(struct rsc *rsci, int seq_num) | ||
469 | { | ||
470 | struct gss_svc_seq_data *sd = &rsci->seqdata; | ||
471 | |||
472 | spin_lock(&sd->sd_lock); | ||
473 | if (seq_num > sd->sd_max) { | ||
474 | if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { | ||
475 | memset(sd->sd_win,0,sizeof(sd->sd_win)); | ||
476 | sd->sd_max = seq_num; | ||
477 | } else while (sd->sd_max < seq_num) { | ||
478 | sd->sd_max++; | ||
479 | __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); | ||
480 | } | ||
481 | __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); | ||
482 | goto ok; | ||
483 | } else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) { | ||
484 | goto drop; | ||
485 | } | ||
486 | /* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */ | ||
487 | if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) | ||
488 | goto drop; | ||
489 | ok: | ||
490 | spin_unlock(&sd->sd_lock); | ||
491 | return 1; | ||
492 | drop: | ||
493 | spin_unlock(&sd->sd_lock); | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static inline u32 round_up_to_quad(u32 i) | ||
498 | { | ||
499 | return (i + 3 ) & ~3; | ||
500 | } | ||
501 | |||
502 | static inline int | ||
503 | svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o) | ||
504 | { | ||
505 | int l; | ||
506 | |||
507 | if (argv->iov_len < 4) | ||
508 | return -1; | ||
509 | o->len = ntohl(svc_getu32(argv)); | ||
510 | l = round_up_to_quad(o->len); | ||
511 | if (argv->iov_len < l) | ||
512 | return -1; | ||
513 | o->data = argv->iov_base; | ||
514 | argv->iov_base += l; | ||
515 | argv->iov_len -= l; | ||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static inline int | ||
520 | svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o) | ||
521 | { | ||
522 | u32 *p; | ||
523 | |||
524 | if (resv->iov_len + 4 > PAGE_SIZE) | ||
525 | return -1; | ||
526 | svc_putu32(resv, htonl(o->len)); | ||
527 | p = resv->iov_base + resv->iov_len; | ||
528 | resv->iov_len += round_up_to_quad(o->len); | ||
529 | if (resv->iov_len > PAGE_SIZE) | ||
530 | return -1; | ||
531 | memcpy(p, o->data, o->len); | ||
532 | memset((u8 *)p + o->len, 0, round_up_to_quad(o->len) - o->len); | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | /* Verify the checksum on the header and return SVC_OK on success. | ||
537 | * Otherwise, return SVC_DROP (in the case of a bad sequence number) | ||
538 | * or return SVC_DENIED and indicate error in authp. | ||
539 | */ | ||
540 | static int | ||
541 | gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, | ||
542 | u32 *rpcstart, struct rpc_gss_wire_cred *gc, u32 *authp) | ||
543 | { | ||
544 | struct gss_ctx *ctx_id = rsci->mechctx; | ||
545 | struct xdr_buf rpchdr; | ||
546 | struct xdr_netobj checksum; | ||
547 | u32 flavor = 0; | ||
548 | struct kvec *argv = &rqstp->rq_arg.head[0]; | ||
549 | struct kvec iov; | ||
550 | |||
551 | /* data to compute the checksum over: */ | ||
552 | iov.iov_base = rpcstart; | ||
553 | iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart; | ||
554 | xdr_buf_from_iov(&iov, &rpchdr); | ||
555 | |||
556 | *authp = rpc_autherr_badverf; | ||
557 | if (argv->iov_len < 4) | ||
558 | return SVC_DENIED; | ||
559 | flavor = ntohl(svc_getu32(argv)); | ||
560 | if (flavor != RPC_AUTH_GSS) | ||
561 | return SVC_DENIED; | ||
562 | if (svc_safe_getnetobj(argv, &checksum)) | ||
563 | return SVC_DENIED; | ||
564 | |||
565 | if (rqstp->rq_deferred) /* skip verification of revisited request */ | ||
566 | return SVC_OK; | ||
567 | if (gss_verify_mic(ctx_id, &rpchdr, &checksum, NULL) | ||
568 | != GSS_S_COMPLETE) { | ||
569 | *authp = rpcsec_gsserr_credproblem; | ||
570 | return SVC_DENIED; | ||
571 | } | ||
572 | |||
573 | if (gc->gc_seq > MAXSEQ) { | ||
574 | dprintk("RPC: svcauth_gss: discarding request with large sequence number %d\n", | ||
575 | gc->gc_seq); | ||
576 | *authp = rpcsec_gsserr_ctxproblem; | ||
577 | return SVC_DENIED; | ||
578 | } | ||
579 | if (!gss_check_seq_num(rsci, gc->gc_seq)) { | ||
580 | dprintk("RPC: svcauth_gss: discarding request with old sequence number %d\n", | ||
581 | gc->gc_seq); | ||
582 | return SVC_DROP; | ||
583 | } | ||
584 | return SVC_OK; | ||
585 | } | ||
586 | |||
587 | static int | ||
588 | gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) | ||
589 | { | ||
590 | u32 xdr_seq; | ||
591 | u32 maj_stat; | ||
592 | struct xdr_buf verf_data; | ||
593 | struct xdr_netobj mic; | ||
594 | u32 *p; | ||
595 | struct kvec iov; | ||
596 | |||
597 | svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_GSS)); | ||
598 | xdr_seq = htonl(seq); | ||
599 | |||
600 | iov.iov_base = &xdr_seq; | ||
601 | iov.iov_len = sizeof(xdr_seq); | ||
602 | xdr_buf_from_iov(&iov, &verf_data); | ||
603 | p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; | ||
604 | mic.data = (u8 *)(p + 1); | ||
605 | maj_stat = gss_get_mic(ctx_id, 0, &verf_data, &mic); | ||
606 | if (maj_stat != GSS_S_COMPLETE) | ||
607 | return -1; | ||
608 | *p++ = htonl(mic.len); | ||
609 | memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); | ||
610 | p += XDR_QUADLEN(mic.len); | ||
611 | if (!xdr_ressize_check(rqstp, p)) | ||
612 | return -1; | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | struct gss_domain { | ||
617 | struct auth_domain h; | ||
618 | u32 pseudoflavor; | ||
619 | }; | ||
620 | |||
621 | static struct auth_domain * | ||
622 | find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) | ||
623 | { | ||
624 | char *name; | ||
625 | |||
626 | name = gss_service_to_auth_domain_name(ctx->mech_type, svc); | ||
627 | if (!name) | ||
628 | return NULL; | ||
629 | return auth_domain_find(name); | ||
630 | } | ||
631 | |||
632 | int | ||
633 | svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) | ||
634 | { | ||
635 | struct gss_domain *new; | ||
636 | struct auth_domain *test; | ||
637 | int stat = -ENOMEM; | ||
638 | |||
639 | new = kmalloc(sizeof(*new), GFP_KERNEL); | ||
640 | if (!new) | ||
641 | goto out; | ||
642 | cache_init(&new->h.h); | ||
643 | new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL); | ||
644 | if (!new->h.name) | ||
645 | goto out_free_dom; | ||
646 | strcpy(new->h.name, name); | ||
647 | new->h.flavour = RPC_AUTH_GSS; | ||
648 | new->pseudoflavor = pseudoflavor; | ||
649 | new->h.h.expiry_time = NEVER; | ||
650 | |||
651 | test = auth_domain_lookup(&new->h, 1); | ||
652 | if (test == &new->h) { | ||
653 | BUG_ON(atomic_dec_and_test(&new->h.h.refcnt)); | ||
654 | } else { /* XXX Duplicate registration? */ | ||
655 | auth_domain_put(&new->h); | ||
656 | goto out; | ||
657 | } | ||
658 | return 0; | ||
659 | |||
660 | out_free_dom: | ||
661 | kfree(new); | ||
662 | out: | ||
663 | return stat; | ||
664 | } | ||
665 | |||
666 | EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor); | ||
667 | |||
668 | static inline int | ||
669 | read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) | ||
670 | { | ||
671 | u32 raw; | ||
672 | int status; | ||
673 | |||
674 | status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); | ||
675 | if (status) | ||
676 | return status; | ||
677 | *obj = ntohl(raw); | ||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | /* It would be nice if this bit of code could be shared with the client. | ||
682 | * Obstacles: | ||
683 | * The client shouldn't malloc(), would have to pass in own memory. | ||
684 | * The server uses base of head iovec as read pointer, while the | ||
685 | * client uses separate pointer. */ | ||
686 | static int | ||
687 | unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) | ||
688 | { | ||
689 | int stat = -EINVAL; | ||
690 | u32 integ_len, maj_stat; | ||
691 | struct xdr_netobj mic; | ||
692 | struct xdr_buf integ_buf; | ||
693 | |||
694 | integ_len = ntohl(svc_getu32(&buf->head[0])); | ||
695 | if (integ_len & 3) | ||
696 | goto out; | ||
697 | if (integ_len > buf->len) | ||
698 | goto out; | ||
699 | if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) | ||
700 | BUG(); | ||
701 | /* copy out mic... */ | ||
702 | if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) | ||
703 | BUG(); | ||
704 | if (mic.len > RPC_MAX_AUTH_SIZE) | ||
705 | goto out; | ||
706 | mic.data = kmalloc(mic.len, GFP_KERNEL); | ||
707 | if (!mic.data) | ||
708 | goto out; | ||
709 | if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) | ||
710 | goto out; | ||
711 | maj_stat = gss_verify_mic(ctx, &integ_buf, &mic, NULL); | ||
712 | if (maj_stat != GSS_S_COMPLETE) | ||
713 | goto out; | ||
714 | if (ntohl(svc_getu32(&buf->head[0])) != seq) | ||
715 | goto out; | ||
716 | stat = 0; | ||
717 | out: | ||
718 | return stat; | ||
719 | } | ||
720 | |||
721 | struct gss_svc_data { | ||
722 | /* decoded gss client cred: */ | ||
723 | struct rpc_gss_wire_cred clcred; | ||
724 | /* pointer to the beginning of the procedure-specific results, | ||
725 | * which may be encrypted/checksummed in svcauth_gss_release: */ | ||
726 | u32 *body_start; | ||
727 | struct rsc *rsci; | ||
728 | }; | ||
729 | |||
730 | static int | ||
731 | svcauth_gss_set_client(struct svc_rqst *rqstp) | ||
732 | { | ||
733 | struct gss_svc_data *svcdata = rqstp->rq_auth_data; | ||
734 | struct rsc *rsci = svcdata->rsci; | ||
735 | struct rpc_gss_wire_cred *gc = &svcdata->clcred; | ||
736 | |||
737 | rqstp->rq_client = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); | ||
738 | if (rqstp->rq_client == NULL) | ||
739 | return SVC_DENIED; | ||
740 | return SVC_OK; | ||
741 | } | ||
742 | |||
743 | /* | ||
744 | * Accept an rpcsec packet. | ||
745 | * If context establishment, punt to user space | ||
746 | * If data exchange, verify/decrypt | ||
747 | * If context destruction, handle here | ||
748 | * In the context establishment and destruction case we encode | ||
749 | * response here and return SVC_COMPLETE. | ||
750 | */ | ||
751 | static int | ||
752 | svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp) | ||
753 | { | ||
754 | struct kvec *argv = &rqstp->rq_arg.head[0]; | ||
755 | struct kvec *resv = &rqstp->rq_res.head[0]; | ||
756 | u32 crlen; | ||
757 | struct xdr_netobj tmpobj; | ||
758 | struct gss_svc_data *svcdata = rqstp->rq_auth_data; | ||
759 | struct rpc_gss_wire_cred *gc; | ||
760 | struct rsc *rsci = NULL; | ||
761 | struct rsi *rsip, rsikey; | ||
762 | u32 *rpcstart; | ||
763 | u32 *reject_stat = resv->iov_base + resv->iov_len; | ||
764 | int ret; | ||
765 | |||
766 | dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n",argv->iov_len); | ||
767 | |||
768 | *authp = rpc_autherr_badcred; | ||
769 | if (!svcdata) | ||
770 | svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); | ||
771 | if (!svcdata) | ||
772 | goto auth_err; | ||
773 | rqstp->rq_auth_data = svcdata; | ||
774 | svcdata->body_start = NULL; | ||
775 | svcdata->rsci = NULL; | ||
776 | gc = &svcdata->clcred; | ||
777 | |||
778 | /* start of rpc packet is 7 u32's back from here: | ||
779 | * xid direction rpcversion prog vers proc flavour | ||
780 | */ | ||
781 | rpcstart = argv->iov_base; | ||
782 | rpcstart -= 7; | ||
783 | |||
784 | /* credential is: | ||
785 | * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle | ||
786 | * at least 5 u32s, and is preceeded by length, so that makes 6. | ||
787 | */ | ||
788 | |||
789 | if (argv->iov_len < 5 * 4) | ||
790 | goto auth_err; | ||
791 | crlen = ntohl(svc_getu32(argv)); | ||
792 | if (ntohl(svc_getu32(argv)) != RPC_GSS_VERSION) | ||
793 | goto auth_err; | ||
794 | gc->gc_proc = ntohl(svc_getu32(argv)); | ||
795 | gc->gc_seq = ntohl(svc_getu32(argv)); | ||
796 | gc->gc_svc = ntohl(svc_getu32(argv)); | ||
797 | if (svc_safe_getnetobj(argv, &gc->gc_ctx)) | ||
798 | goto auth_err; | ||
799 | if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4) | ||
800 | goto auth_err; | ||
801 | |||
802 | if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0)) | ||
803 | goto auth_err; | ||
804 | |||
805 | /* | ||
806 | * We've successfully parsed the credential. Let's check out the | ||
807 | * verifier. An AUTH_NULL verifier is allowed (and required) for | ||
808 | * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for | ||
809 | * PROC_DATA and PROC_DESTROY. | ||
810 | * | ||
811 | * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length). | ||
812 | * AUTH_RPCSEC_GSS verifier is: | ||
813 | * 6 (AUTH_RPCSEC_GSS), length, checksum. | ||
814 | * checksum is calculated over rpcheader from xid up to here. | ||
815 | */ | ||
816 | *authp = rpc_autherr_badverf; | ||
817 | switch (gc->gc_proc) { | ||
818 | case RPC_GSS_PROC_INIT: | ||
819 | case RPC_GSS_PROC_CONTINUE_INIT: | ||
820 | if (argv->iov_len < 2 * 4) | ||
821 | goto auth_err; | ||
822 | if (ntohl(svc_getu32(argv)) != RPC_AUTH_NULL) | ||
823 | goto auth_err; | ||
824 | if (ntohl(svc_getu32(argv)) != 0) | ||
825 | goto auth_err; | ||
826 | break; | ||
827 | case RPC_GSS_PROC_DATA: | ||
828 | case RPC_GSS_PROC_DESTROY: | ||
829 | *authp = rpcsec_gsserr_credproblem; | ||
830 | rsci = gss_svc_searchbyctx(&gc->gc_ctx); | ||
831 | if (!rsci) | ||
832 | goto auth_err; | ||
833 | switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { | ||
834 | case SVC_OK: | ||
835 | break; | ||
836 | case SVC_DENIED: | ||
837 | goto auth_err; | ||
838 | case SVC_DROP: | ||
839 | goto drop; | ||
840 | } | ||
841 | break; | ||
842 | default: | ||
843 | *authp = rpc_autherr_rejectedcred; | ||
844 | goto auth_err; | ||
845 | } | ||
846 | |||
847 | /* now act upon the command: */ | ||
848 | switch (gc->gc_proc) { | ||
849 | case RPC_GSS_PROC_INIT: | ||
850 | case RPC_GSS_PROC_CONTINUE_INIT: | ||
851 | *authp = rpc_autherr_badcred; | ||
852 | if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) | ||
853 | goto auth_err; | ||
854 | memset(&rsikey, 0, sizeof(rsikey)); | ||
855 | if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) | ||
856 | goto drop; | ||
857 | *authp = rpc_autherr_badverf; | ||
858 | if (svc_safe_getnetobj(argv, &tmpobj)) { | ||
859 | kfree(rsikey.in_handle.data); | ||
860 | goto auth_err; | ||
861 | } | ||
862 | if (dup_netobj(&rsikey.in_token, &tmpobj)) { | ||
863 | kfree(rsikey.in_handle.data); | ||
864 | goto drop; | ||
865 | } | ||
866 | |||
867 | rsip = rsi_lookup(&rsikey, 0); | ||
868 | rsi_free(&rsikey); | ||
869 | if (!rsip) { | ||
870 | goto drop; | ||
871 | } | ||
872 | switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) { | ||
873 | case -EAGAIN: | ||
874 | goto drop; | ||
875 | case -ENOENT: | ||
876 | goto drop; | ||
877 | case 0: | ||
878 | rsci = gss_svc_searchbyctx(&rsip->out_handle); | ||
879 | if (!rsci) { | ||
880 | goto drop; | ||
881 | } | ||
882 | if (gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN)) | ||
883 | goto drop; | ||
884 | if (resv->iov_len + 4 > PAGE_SIZE) | ||
885 | goto drop; | ||
886 | svc_putu32(resv, rpc_success); | ||
887 | if (svc_safe_putnetobj(resv, &rsip->out_handle)) | ||
888 | goto drop; | ||
889 | if (resv->iov_len + 3 * 4 > PAGE_SIZE) | ||
890 | goto drop; | ||
891 | svc_putu32(resv, htonl(rsip->major_status)); | ||
892 | svc_putu32(resv, htonl(rsip->minor_status)); | ||
893 | svc_putu32(resv, htonl(GSS_SEQ_WIN)); | ||
894 | if (svc_safe_putnetobj(resv, &rsip->out_token)) | ||
895 | goto drop; | ||
896 | rqstp->rq_client = NULL; | ||
897 | } | ||
898 | goto complete; | ||
899 | case RPC_GSS_PROC_DESTROY: | ||
900 | set_bit(CACHE_NEGATIVE, &rsci->h.flags); | ||
901 | if (resv->iov_len + 4 > PAGE_SIZE) | ||
902 | goto drop; | ||
903 | svc_putu32(resv, rpc_success); | ||
904 | goto complete; | ||
905 | case RPC_GSS_PROC_DATA: | ||
906 | *authp = rpcsec_gsserr_ctxproblem; | ||
907 | if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) | ||
908 | goto auth_err; | ||
909 | rqstp->rq_cred = rsci->cred; | ||
910 | get_group_info(rsci->cred.cr_group_info); | ||
911 | *authp = rpc_autherr_badcred; | ||
912 | switch (gc->gc_svc) { | ||
913 | case RPC_GSS_SVC_NONE: | ||
914 | break; | ||
915 | case RPC_GSS_SVC_INTEGRITY: | ||
916 | if (unwrap_integ_data(&rqstp->rq_arg, | ||
917 | gc->gc_seq, rsci->mechctx)) | ||
918 | goto auth_err; | ||
919 | /* placeholders for length and seq. number: */ | ||
920 | svcdata->body_start = resv->iov_base + resv->iov_len; | ||
921 | svc_putu32(resv, 0); | ||
922 | svc_putu32(resv, 0); | ||
923 | break; | ||
924 | case RPC_GSS_SVC_PRIVACY: | ||
925 | /* currently unsupported */ | ||
926 | default: | ||
927 | goto auth_err; | ||
928 | } | ||
929 | svcdata->rsci = rsci; | ||
930 | cache_get(&rsci->h); | ||
931 | ret = SVC_OK; | ||
932 | goto out; | ||
933 | } | ||
934 | auth_err: | ||
935 | /* Restore write pointer to original value: */ | ||
936 | xdr_ressize_check(rqstp, reject_stat); | ||
937 | ret = SVC_DENIED; | ||
938 | goto out; | ||
939 | complete: | ||
940 | ret = SVC_COMPLETE; | ||
941 | goto out; | ||
942 | drop: | ||
943 | ret = SVC_DROP; | ||
944 | out: | ||
945 | if (rsci) | ||
946 | rsc_put(&rsci->h, &rsc_cache); | ||
947 | return ret; | ||
948 | } | ||
949 | |||
950 | static int | ||
951 | svcauth_gss_release(struct svc_rqst *rqstp) | ||
952 | { | ||
953 | struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; | ||
954 | struct rpc_gss_wire_cred *gc = &gsd->clcred; | ||
955 | struct xdr_buf *resbuf = &rqstp->rq_res; | ||
956 | struct xdr_buf integ_buf; | ||
957 | struct xdr_netobj mic; | ||
958 | struct kvec *resv; | ||
959 | u32 *p; | ||
960 | int integ_offset, integ_len; | ||
961 | int stat = -EINVAL; | ||
962 | |||
963 | if (gc->gc_proc != RPC_GSS_PROC_DATA) | ||
964 | goto out; | ||
965 | /* Release can be called twice, but we only wrap once. */ | ||
966 | if (gsd->body_start == NULL) | ||
967 | goto out; | ||
968 | /* normally not set till svc_send, but we need it here: */ | ||
969 | resbuf->len = resbuf->head[0].iov_len | ||
970 | + resbuf->page_len + resbuf->tail[0].iov_len; | ||
971 | switch (gc->gc_svc) { | ||
972 | case RPC_GSS_SVC_NONE: | ||
973 | break; | ||
974 | case RPC_GSS_SVC_INTEGRITY: | ||
975 | p = gsd->body_start; | ||
976 | gsd->body_start = NULL; | ||
977 | /* move accept_stat to right place: */ | ||
978 | memcpy(p, p + 2, 4); | ||
979 | /* don't wrap in failure case: */ | ||
980 | /* Note: counting on not getting here if call was not even | ||
981 | * accepted! */ | ||
982 | if (*p != rpc_success) { | ||
983 | resbuf->head[0].iov_len -= 2 * 4; | ||
984 | goto out; | ||
985 | } | ||
986 | p++; | ||
987 | integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; | ||
988 | integ_len = resbuf->len - integ_offset; | ||
989 | BUG_ON(integ_len % 4); | ||
990 | *p++ = htonl(integ_len); | ||
991 | *p++ = htonl(gc->gc_seq); | ||
992 | if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, | ||
993 | integ_len)) | ||
994 | BUG(); | ||
995 | if (resbuf->page_len == 0 | ||
996 | && resbuf->tail[0].iov_len + RPC_MAX_AUTH_SIZE | ||
997 | < PAGE_SIZE) { | ||
998 | BUG_ON(resbuf->tail[0].iov_len); | ||
999 | /* Use head for everything */ | ||
1000 | resv = &resbuf->head[0]; | ||
1001 | } else if (resbuf->tail[0].iov_base == NULL) { | ||
1002 | /* copied from nfsd4_encode_read */ | ||
1003 | svc_take_page(rqstp); | ||
1004 | resbuf->tail[0].iov_base = page_address(rqstp | ||
1005 | ->rq_respages[rqstp->rq_resused-1]); | ||
1006 | rqstp->rq_restailpage = rqstp->rq_resused-1; | ||
1007 | resbuf->tail[0].iov_len = 0; | ||
1008 | resv = &resbuf->tail[0]; | ||
1009 | } else { | ||
1010 | resv = &resbuf->tail[0]; | ||
1011 | } | ||
1012 | mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; | ||
1013 | if (gss_get_mic(gsd->rsci->mechctx, 0, &integ_buf, &mic)) | ||
1014 | goto out_err; | ||
1015 | svc_putu32(resv, htonl(mic.len)); | ||
1016 | memset(mic.data + mic.len, 0, | ||
1017 | round_up_to_quad(mic.len) - mic.len); | ||
1018 | resv->iov_len += XDR_QUADLEN(mic.len) << 2; | ||
1019 | /* not strictly required: */ | ||
1020 | resbuf->len += XDR_QUADLEN(mic.len) << 2; | ||
1021 | BUG_ON(resv->iov_len > PAGE_SIZE); | ||
1022 | break; | ||
1023 | case RPC_GSS_SVC_PRIVACY: | ||
1024 | default: | ||
1025 | goto out_err; | ||
1026 | } | ||
1027 | |||
1028 | out: | ||
1029 | stat = 0; | ||
1030 | out_err: | ||
1031 | if (rqstp->rq_client) | ||
1032 | auth_domain_put(rqstp->rq_client); | ||
1033 | rqstp->rq_client = NULL; | ||
1034 | if (rqstp->rq_cred.cr_group_info) | ||
1035 | put_group_info(rqstp->rq_cred.cr_group_info); | ||
1036 | rqstp->rq_cred.cr_group_info = NULL; | ||
1037 | if (gsd->rsci) | ||
1038 | rsc_put(&gsd->rsci->h, &rsc_cache); | ||
1039 | gsd->rsci = NULL; | ||
1040 | |||
1041 | return stat; | ||
1042 | } | ||
1043 | |||
1044 | static void | ||
1045 | svcauth_gss_domain_release(struct auth_domain *dom) | ||
1046 | { | ||
1047 | struct gss_domain *gd = container_of(dom, struct gss_domain, h); | ||
1048 | |||
1049 | kfree(dom->name); | ||
1050 | kfree(gd); | ||
1051 | } | ||
1052 | |||
1053 | static struct auth_ops svcauthops_gss = { | ||
1054 | .name = "rpcsec_gss", | ||
1055 | .owner = THIS_MODULE, | ||
1056 | .flavour = RPC_AUTH_GSS, | ||
1057 | .accept = svcauth_gss_accept, | ||
1058 | .release = svcauth_gss_release, | ||
1059 | .domain_release = svcauth_gss_domain_release, | ||
1060 | .set_client = svcauth_gss_set_client, | ||
1061 | }; | ||
1062 | |||
1063 | int | ||
1064 | gss_svc_init(void) | ||
1065 | { | ||
1066 | int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); | ||
1067 | if (rv == 0) { | ||
1068 | cache_register(&rsc_cache); | ||
1069 | cache_register(&rsi_cache); | ||
1070 | } | ||
1071 | return rv; | ||
1072 | } | ||
1073 | |||
1074 | void | ||
1075 | gss_svc_shutdown(void) | ||
1076 | { | ||
1077 | cache_unregister(&rsc_cache); | ||
1078 | cache_unregister(&rsi_cache); | ||
1079 | svc_auth_unregister(RPC_AUTH_GSS); | ||
1080 | } | ||