aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc/auth_gss
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /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/Makefile18
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c1152
-rw-r--r--net/sunrpc/auth_gss/gss_generic_token.c235
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c209
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_mech.c275
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c176
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seqnum.c88
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c202
-rw-r--r--net/sunrpc/auth_gss/gss_mech_switch.c301
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_mech.c300
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_seal.c132
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_token.c266
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_unseal.c128
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c1080
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
5obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
6
7auth_rpcgss-objs := auth_gss.o gss_generic_token.o \
8 gss_mech_switch.o svcauth_gss.o gss_krb5_crypto.o
9
10obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
11
12rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
13 gss_krb5_seqnum.o
14
15obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
16
17rpcsec_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
58static struct rpc_authops authgss_ops;
59
60static 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
83static DEFINE_RWLOCK(gss_ctx_lock);
84
85struct 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
96static void gss_destroy_ctx(struct gss_cl_ctx *);
97static struct rpc_pipe_ops gss_upcall_ops;
98
99void
100print_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
137EXPORT_SYMBOL(print_hexl);
138
139static inline struct gss_cl_ctx *
140gss_get_ctx(struct gss_cl_ctx *ctx)
141{
142 atomic_inc(&ctx->count);
143 return ctx;
144}
145
146static inline void
147gss_put_ctx(struct gss_cl_ctx *ctx)
148{
149 if (atomic_dec_and_test(&ctx->count))
150 gss_destroy_ctx(ctx);
151}
152
153static void
154gss_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
167static int
168gss_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
180static const void *
181simple_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
190static inline const void *
191simple_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
210static struct gss_cl_ctx *
211gss_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
223static struct gss_cl_ctx *
224gss_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)
240static const void *
241gss_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;
286err:
287 dprintk("RPC: gss_fill_context returning %ld\n", -PTR_ERR(p));
288 return p;
289}
290
291
292struct 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
303static void
304gss_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
314static 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 */
333static inline struct gss_upcall_msg *
334gss_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
349static 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
360static void
361gss_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
370static void
371gss_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
389static inline struct gss_upcall_msg *
390gss_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
409static struct gss_upcall_msg *
410gss_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
429static inline int
430gss_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);
459out:
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
465static inline int
466gss_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;
497out_intr:
498 finish_wait(&gss_msg->waitqueue, &wait);
499 gss_release_msg(gss_msg);
500out:
501 dprintk("RPC: gss_create_upcall for uid %u result %d\n", cred->cr_uid, err);
502 return err;
503}
504
505static ssize_t
506gss_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
528static ssize_t
529gss_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;
595err_put_ctx:
596 gss_put_ctx(ctx);
597err:
598 kfree(buf);
599out:
600 dprintk("RPC: gss_pipe_downcall returning %d\n", err);
601 return err;
602}
603
604static void
605gss_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
631static void
632gss_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 */
658static struct rpc_auth *
659gss_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;
701err_put_mech:
702 gss_mech_put(gss_auth->mech);
703err_free:
704 kfree(gss_auth);
705out_dec:
706 module_put(THIS_MODULE);
707 return NULL;
708}
709
710static void
711gss_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. */
730static void
731gss_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
742static void
743gss_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 */
757static struct rpc_cred *
758gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags)
759{
760 return rpcauth_lookup_credcache(auth, acred, taskflags);
761}
762
763static struct rpc_cred *
764gss_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
792out_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
798static int
799gss_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*/
813static u32 *
814gss_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;
868out_put_ctx:
869 gss_put_ctx(ctx);
870 return NULL;
871}
872
873/*
874* Refresh credentials. XXX - finish
875*/
876static int
877gss_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
885static u32 *
886gss_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);
934out_bad:
935 gss_put_ctx(ctx);
936 dprintk("RPC: %4u gss_validate failed.\n", task->tk_pid);
937 return NULL;
938}
939
940static inline int
941gss_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
989static int
990gss_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 }
1018out:
1019 gss_put_ctx(ctx);
1020 dprintk("RPC: %4u gss_wrap_req returning %d\n", task->tk_pid, status);
1021 return status;
1022}
1023
1024static inline int
1025gss_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
1062static int
1063gss_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 }
1085out_decode:
1086 status = decode(rqstp, p, obj);
1087out:
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
1094static 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
1106static 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
1117static 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 */
1127static 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;
1138out_unregister:
1139 rpcauth_unregister(&authgss_ops);
1140out:
1141 return err;
1142}
1143
1144static void __exit exit_rpcsec_gss(void)
1145{
1146 gss_svc_shutdown();
1147 rpcauth_unregister(&authgss_ops);
1148}
1149
1150MODULE_LICENSE("GPL");
1151module_init(init_rpcsec_gss)
1152module_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
590x60 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
69For the purposes of this abstraction, the token "header" consists of
70the sequence tag and length octets, the mech OID DER encoding, and the
71first two inner bytes, which indicate the token type. The token
72"body" consists of everything else.
73
74*/
75
76static int
77der_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
96static void
97der_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
118static int
119der_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
147int
148g_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
155EXPORT_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
160void
161g_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
170EXPORT_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 */
180u32
181g_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
234EXPORT_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
50u32
51krb5_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);
86out:
87 dprintk("RPC: krb5_encrypt returns %d\n",ret);
88 return(ret);
89}
90
91EXPORT_SYMBOL(krb5_encrypt);
92
93u32
94krb5_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);
128out:
129 dprintk("RPC: gss_k5decrypt returns %d\n",ret);
130 return(ret);
131}
132
133EXPORT_SYMBOL(krb5_decrypt);
134
135static void
136buf_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 */
143s32
144make_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;
203out:
204 if (tfm)
205 crypto_free_tfm(tfm);
206 return code;
207}
208
209EXPORT_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
51static const void *
52simple_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
61static const void *
62simple_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
81static inline const void *
82get_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
112out_err_free_tfm:
113 crypto_free_tfm(*res);
114out_err_free_key:
115 kfree(key.data);
116 p = ERR_PTR(-EINVAL);
117out_err:
118 return p;
119}
120
121static int
122gss_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
172out_err_free_key2:
173 crypto_free_tfm(ctx->seq);
174out_err_free_key1:
175 crypto_free_tfm(ctx->enc);
176out_err_free_mech:
177 kfree(ctx->mech_used.data);
178out_err_free_ctx:
179 kfree(ctx);
180out_err:
181 return PTR_ERR(p);
182}
183
184static void
185gss_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
197static u32
198gss_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
215static u32
216gss_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
230static 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
237static 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
250static 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
258static 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
268static void __exit cleanup_kerberos_module(void)
269{
270 gss_mech_unregister(&gss_kerberos_mech);
271}
272
273MODULE_LICENSE("GPL");
274module_init(init_kerberos_module);
275module_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
73static inline int
74gss_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
81u32
82krb5_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);
173out_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
43s32
44krb5_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
64s32
65krb5_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). */
80u32
81krb5_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;
199out:
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
53static LIST_HEAD(registered_mechs);
54static DEFINE_SPINLOCK(registered_mechs_lock);
55
56static void
57gss_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
70static inline char *
71make_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
84static int
85gss_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;
102out:
103 gss_mech_free(gm);
104 return status;
105}
106
107int
108gss_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(&registered_mechs_lock);
116 list_add(&gm->gm_list, &registered_mechs);
117 spin_unlock(&registered_mechs_lock);
118 dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
119 return 0;
120}
121
122EXPORT_SYMBOL(gss_mech_register);
123
124void
125gss_mech_unregister(struct gss_api_mech *gm)
126{
127 spin_lock(&registered_mechs_lock);
128 list_del(&gm->gm_list);
129 spin_unlock(&registered_mechs_lock);
130 dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
131 gss_mech_free(gm);
132}
133
134EXPORT_SYMBOL(gss_mech_unregister);
135
136struct gss_api_mech *
137gss_mech_get(struct gss_api_mech *gm)
138{
139 __module_get(gm->gm_owner);
140 return gm;
141}
142
143EXPORT_SYMBOL(gss_mech_get);
144
145struct gss_api_mech *
146gss_mech_get_by_name(const char *name)
147{
148 struct gss_api_mech *pos, *gm = NULL;
149
150 spin_lock(&registered_mechs_lock);
151 list_for_each_entry(pos, &registered_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(&registered_mechs_lock);
159 return gm;
160
161}
162
163EXPORT_SYMBOL(gss_mech_get_by_name);
164
165static inline int
166mech_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
177struct gss_api_mech *
178gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
179{
180 struct gss_api_mech *pos, *gm = NULL;
181
182 spin_lock(&registered_mechs_lock);
183 list_for_each_entry(pos, &registered_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(&registered_mechs_lock);
193 return gm;
194}
195
196EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
197
198u32
199gss_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
210EXPORT_SYMBOL(gss_pseudoflavor_to_service);
211
212char *
213gss_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
224EXPORT_SYMBOL(gss_service_to_auth_domain_name);
225
226void
227gss_mech_put(struct gss_api_mech * gm)
228{
229 module_put(gm->gm_owner);
230}
231
232EXPORT_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. */
236int
237gss_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
252u32
253gss_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
267u32
268gss_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
284u32
285gss_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
52static const void *
53simple_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
62static const void *
63simple_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
85static inline const void *
86get_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
128out_err_free_tfm:
129 crypto_free_tfm(*res);
130out_err_free_key:
131 if(key.len > 0)
132 kfree(key.data);
133 p = ERR_PTR(-EINVAL);
134out_err:
135 return p;
136}
137
138static int
139gss_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
197out_err_free_key2:
198 crypto_free_tfm(ctx->derived_integ_key);
199out_err_free_key1:
200 crypto_free_tfm(ctx->derived_conf_key);
201out_err_free_s_key:
202 kfree(ctx->share_key.data);
203out_err_free_mech:
204 kfree(ctx->mech_used.data);
205out_err_free_ctx_id:
206 kfree(ctx->ctx_id.data);
207out_err_free_ctx:
208 kfree(ctx);
209out_err:
210 return PTR_ERR(p);
211}
212
213static void
214gss_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
228static u32
229gss_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
248static u32
249gss_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
263static 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
270static 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
275static 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
283static 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
293static void __exit cleanup_spkm3_module(void)
294{
295 gss_mech_unregister(&gss_spkm3_mech);
296}
297
298MODULE_LICENSE("GPL");
299module_init(init_spkm3_module);
300module_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
53u32
54spkm3_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;
126out_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 */
52void
53asn1_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 */
90int
91decode_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 */
155void
156spkm3_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 */
186void
187spkm3_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
202u32
203spkm3_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;
261out:
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 */
51u32
52spkm3_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;
122out:
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
63static 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
72struct 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
79static struct cache_head *rsi_table[RSI_HASHMAX];
80static struct cache_detail rsi_cache;
81static struct rsi *rsi_lookup(struct rsi *item, int set);
82
83static 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
91static 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
100static 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
106static 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
112static 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
123static 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
128static 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
144static 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
160static 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
172static 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;
245out:
246 rsi_free(&rsii);
247 if (rsip)
248 rsi_put(&rsip->h, &rsi_cache);
249 return status;
250}
251
252static 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
261static 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
276struct 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
285struct 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
293static struct cache_head *rsc_table[RSC_HASHMAX];
294static struct cache_detail rsc_cache;
295static struct rsc *rsc_lookup(struct rsc *item, int set);
296
297static 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
306static 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
316static inline int
317rsc_hash(struct rsc *rsci)
318{
319 return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS);
320}
321
322static inline int
323rsc_match(struct rsc *new, struct rsc *tmp)
324{
325 return netobj_equal(&new->handle, &tmp->handle);
326}
327
328static inline void
329rsc_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
339static inline void
340rsc_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
350static 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;
431out:
432 rsc_free(&rsci);
433 if (rscp)
434 rsc_put(&rscp->h, &rsc_cache);
435 return status;
436}
437
438static 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
446static DefineSimpleCacheLookup(rsc, 0);
447
448static struct rsc *
449gss_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. */
467static int
468gss_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;
489ok:
490 spin_unlock(&sd->sd_lock);
491 return 1;
492drop:
493 spin_unlock(&sd->sd_lock);
494 return 0;
495}
496
497static inline u32 round_up_to_quad(u32 i)
498{
499 return (i + 3 ) & ~3;
500}
501
502static inline int
503svc_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
519static inline int
520svc_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 */
540static int
541gss_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
587static int
588gss_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
616struct gss_domain {
617 struct auth_domain h;
618 u32 pseudoflavor;
619};
620
621static struct auth_domain *
622find_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
632int
633svcauth_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
660out_free_dom:
661 kfree(new);
662out:
663 return stat;
664}
665
666EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
667
668static inline int
669read_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. */
686static int
687unwrap_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;
717out:
718 return stat;
719}
720
721struct 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
730static int
731svcauth_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 */
751static int
752svcauth_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 }
934auth_err:
935 /* Restore write pointer to original value: */
936 xdr_ressize_check(rqstp, reject_stat);
937 ret = SVC_DENIED;
938 goto out;
939complete:
940 ret = SVC_COMPLETE;
941 goto out;
942drop:
943 ret = SVC_DROP;
944out:
945 if (rsci)
946 rsc_put(&rsci->h, &rsc_cache);
947 return ret;
948}
949
950static int
951svcauth_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
1028out:
1029 stat = 0;
1030out_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
1044static void
1045svcauth_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
1053static 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
1063int
1064gss_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
1074void
1075gss_svc_shutdown(void)
1076{
1077 cache_unregister(&rsc_cache);
1078 cache_unregister(&rsi_cache);
1079 svc_auth_unregister(RPC_AUTH_GSS);
1080}