CRED: Separate task security context from task_struct
[linux-3.10.git] / security / keys / process_keys.c
1 /* Management of a process's keyrings
2  *
3  * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/sched.h>
15 #include <linux/slab.h>
16 #include <linux/keyctl.h>
17 #include <linux/fs.h>
18 #include <linux/err.h>
19 #include <linux/mutex.h>
20 #include <asm/uaccess.h>
21 #include "internal.h"
22
23 /* session keyring create vs join semaphore */
24 static DEFINE_MUTEX(key_session_mutex);
25
26 /* user keyring creation semaphore */
27 static DEFINE_MUTEX(key_user_keyring_mutex);
28
29 /* the root user's tracking struct */
30 struct key_user root_key_user = {
31         .usage          = ATOMIC_INIT(3),
32         .cons_lock      = __MUTEX_INITIALIZER(root_key_user.cons_lock),
33         .lock           = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
34         .nkeys          = ATOMIC_INIT(2),
35         .nikeys         = ATOMIC_INIT(2),
36         .uid            = 0,
37 };
38
39 /*****************************************************************************/
40 /*
41  * install user and user session keyrings for a particular UID
42  */
43 int install_user_keyrings(void)
44 {
45         struct user_struct *user = current->cred->user;
46         struct key *uid_keyring, *session_keyring;
47         char buf[20];
48         int ret;
49
50         kenter("%p{%u}", user, user->uid);
51
52         if (user->uid_keyring) {
53                 kleave(" = 0 [exist]");
54                 return 0;
55         }
56
57         mutex_lock(&key_user_keyring_mutex);
58         ret = 0;
59
60         if (!user->uid_keyring) {
61                 /* get the UID-specific keyring
62                  * - there may be one in existence already as it may have been
63                  *   pinned by a session, but the user_struct pointing to it
64                  *   may have been destroyed by setuid */
65                 sprintf(buf, "_uid.%u", user->uid);
66
67                 uid_keyring = find_keyring_by_name(buf, true);
68                 if (IS_ERR(uid_keyring)) {
69                         uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
70                                                     current, KEY_ALLOC_IN_QUOTA,
71                                                     NULL);
72                         if (IS_ERR(uid_keyring)) {
73                                 ret = PTR_ERR(uid_keyring);
74                                 goto error;
75                         }
76                 }
77
78                 /* get a default session keyring (which might also exist
79                  * already) */
80                 sprintf(buf, "_uid_ses.%u", user->uid);
81
82                 session_keyring = find_keyring_by_name(buf, true);
83                 if (IS_ERR(session_keyring)) {
84                         session_keyring =
85                                 keyring_alloc(buf, user->uid, (gid_t) -1,
86                                               current, KEY_ALLOC_IN_QUOTA,
87                                               NULL);
88                         if (IS_ERR(session_keyring)) {
89                                 ret = PTR_ERR(session_keyring);
90                                 goto error_release;
91                         }
92
93                         /* we install a link from the user session keyring to
94                          * the user keyring */
95                         ret = key_link(session_keyring, uid_keyring);
96                         if (ret < 0)
97                                 goto error_release_both;
98                 }
99
100                 /* install the keyrings */
101                 user->uid_keyring = uid_keyring;
102                 user->session_keyring = session_keyring;
103         }
104
105         mutex_unlock(&key_user_keyring_mutex);
106         kleave(" = 0");
107         return 0;
108
109 error_release_both:
110         key_put(session_keyring);
111 error_release:
112         key_put(uid_keyring);
113 error:
114         mutex_unlock(&key_user_keyring_mutex);
115         kleave(" = %d", ret);
116         return ret;
117 }
118
119 /*****************************************************************************/
120 /*
121  * deal with the UID changing
122  */
123 void switch_uid_keyring(struct user_struct *new_user)
124 {
125 #if 0 /* do nothing for now */
126         struct key *old;
127
128         /* switch to the new user's session keyring if we were running under
129          * root's default session keyring */
130         if (new_user->uid != 0 &&
131             current->session_keyring == &root_session_keyring
132             ) {
133                 atomic_inc(&new_user->session_keyring->usage);
134
135                 task_lock(current);
136                 old = current->session_keyring;
137                 current->session_keyring = new_user->session_keyring;
138                 task_unlock(current);
139
140                 key_put(old);
141         }
142 #endif
143
144 } /* end switch_uid_keyring() */
145
146 /*****************************************************************************/
147 /*
148  * install a fresh thread keyring, discarding the old one
149  */
150 int install_thread_keyring(void)
151 {
152         struct task_struct *tsk = current;
153         struct key *keyring, *old;
154         char buf[20];
155         int ret;
156
157         sprintf(buf, "_tid.%u", tsk->pid);
158
159         keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
160                                 KEY_ALLOC_QUOTA_OVERRUN, NULL);
161         if (IS_ERR(keyring)) {
162                 ret = PTR_ERR(keyring);
163                 goto error;
164         }
165
166         task_lock(tsk);
167         old = tsk->cred->thread_keyring;
168         tsk->cred->thread_keyring = keyring;
169         task_unlock(tsk);
170
171         ret = 0;
172
173         key_put(old);
174 error:
175         return ret;
176
177 } /* end install_thread_keyring() */
178
179 /*****************************************************************************/
180 /*
181  * make sure a process keyring is installed
182  */
183 int install_process_keyring(void)
184 {
185         struct task_struct *tsk = current;
186         struct key *keyring;
187         char buf[20];
188         int ret;
189
190         might_sleep();
191
192         if (!tsk->signal->process_keyring) {
193                 sprintf(buf, "_pid.%u", tsk->tgid);
194
195                 keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
196                                         KEY_ALLOC_QUOTA_OVERRUN, NULL);
197                 if (IS_ERR(keyring)) {
198                         ret = PTR_ERR(keyring);
199                         goto error;
200                 }
201
202                 /* attach keyring */
203                 spin_lock_irq(&tsk->sighand->siglock);
204                 if (!tsk->signal->process_keyring) {
205                         tsk->signal->process_keyring = keyring;
206                         keyring = NULL;
207                 }
208                 spin_unlock_irq(&tsk->sighand->siglock);
209
210                 key_put(keyring);
211         }
212
213         ret = 0;
214 error:
215         return ret;
216
217 } /* end install_process_keyring() */
218
219 /*****************************************************************************/
220 /*
221  * install a session keyring, discarding the old one
222  * - if a keyring is not supplied, an empty one is invented
223  */
224 static int install_session_keyring(struct key *keyring)
225 {
226         struct task_struct *tsk = current;
227         unsigned long flags;
228         struct key *old;
229         char buf[20];
230
231         might_sleep();
232
233         /* create an empty session keyring */
234         if (!keyring) {
235                 sprintf(buf, "_ses.%u", tsk->tgid);
236
237                 flags = KEY_ALLOC_QUOTA_OVERRUN;
238                 if (tsk->signal->session_keyring)
239                         flags = KEY_ALLOC_IN_QUOTA;
240
241                 keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
242                                         flags, NULL);
243                 if (IS_ERR(keyring))
244                         return PTR_ERR(keyring);
245         }
246         else {
247                 atomic_inc(&keyring->usage);
248         }
249
250         /* install the keyring */
251         spin_lock_irq(&tsk->sighand->siglock);
252         old = tsk->signal->session_keyring;
253         rcu_assign_pointer(tsk->signal->session_keyring, keyring);
254         spin_unlock_irq(&tsk->sighand->siglock);
255
256         /* we're using RCU on the pointer, but there's no point synchronising
257          * on it if it didn't previously point to anything */
258         if (old) {
259                 synchronize_rcu();
260                 key_put(old);
261         }
262
263         return 0;
264
265 } /* end install_session_keyring() */
266
267 /*****************************************************************************/
268 /*
269  * copy the keys in a thread group for fork without CLONE_THREAD
270  */
271 int copy_thread_group_keys(struct task_struct *tsk)
272 {
273         key_check(current->thread_group->session_keyring);
274         key_check(current->thread_group->process_keyring);
275
276         /* no process keyring yet */
277         tsk->signal->process_keyring = NULL;
278
279         /* same session keyring */
280         rcu_read_lock();
281         tsk->signal->session_keyring =
282                 key_get(rcu_dereference(current->signal->session_keyring));
283         rcu_read_unlock();
284
285         return 0;
286
287 } /* end copy_thread_group_keys() */
288
289 /*****************************************************************************/
290 /*
291  * copy the keys for fork
292  */
293 int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
294 {
295         key_check(tsk->cred->thread_keyring);
296         key_check(tsk->cred->request_key_auth);
297
298         /* no thread keyring yet */
299         tsk->cred->thread_keyring = NULL;
300
301         /* copy the request_key() authorisation for this thread */
302         key_get(tsk->cred->request_key_auth);
303
304         return 0;
305
306 } /* end copy_keys() */
307
308 /*****************************************************************************/
309 /*
310  * dispose of thread group keys upon thread group destruction
311  */
312 void exit_thread_group_keys(struct signal_struct *tg)
313 {
314         key_put(tg->session_keyring);
315         key_put(tg->process_keyring);
316
317 } /* end exit_thread_group_keys() */
318
319 /*****************************************************************************/
320 /*
321  * dispose of per-thread keys upon thread exit
322  */
323 void exit_keys(struct task_struct *tsk)
324 {
325         key_put(tsk->cred->thread_keyring);
326         key_put(tsk->cred->request_key_auth);
327
328 } /* end exit_keys() */
329
330 /*****************************************************************************/
331 /*
332  * deal with execve()
333  */
334 int exec_keys(struct task_struct *tsk)
335 {
336         struct key *old;
337
338         /* newly exec'd tasks don't get a thread keyring */
339         task_lock(tsk);
340         old = tsk->cred->thread_keyring;
341         tsk->cred->thread_keyring = NULL;
342         task_unlock(tsk);
343
344         key_put(old);
345
346         /* discard the process keyring from a newly exec'd task */
347         spin_lock_irq(&tsk->sighand->siglock);
348         old = tsk->signal->process_keyring;
349         tsk->signal->process_keyring = NULL;
350         spin_unlock_irq(&tsk->sighand->siglock);
351
352         key_put(old);
353
354         return 0;
355
356 } /* end exec_keys() */
357
358 /*****************************************************************************/
359 /*
360  * deal with SUID programs
361  * - we might want to make this invent a new session keyring
362  */
363 int suid_keys(struct task_struct *tsk)
364 {
365         return 0;
366
367 } /* end suid_keys() */
368
369 /*****************************************************************************/
370 /*
371  * the filesystem user ID changed
372  */
373 void key_fsuid_changed(struct task_struct *tsk)
374 {
375         /* update the ownership of the thread keyring */
376         BUG_ON(!tsk->cred);
377         if (tsk->cred->thread_keyring) {
378                 down_write(&tsk->cred->thread_keyring->sem);
379                 tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
380                 up_write(&tsk->cred->thread_keyring->sem);
381         }
382
383 } /* end key_fsuid_changed() */
384
385 /*****************************************************************************/
386 /*
387  * the filesystem group ID changed
388  */
389 void key_fsgid_changed(struct task_struct *tsk)
390 {
391         /* update the ownership of the thread keyring */
392         BUG_ON(!tsk->cred);
393         if (tsk->cred->thread_keyring) {
394                 down_write(&tsk->cred->thread_keyring->sem);
395                 tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
396                 up_write(&tsk->cred->thread_keyring->sem);
397         }
398
399 } /* end key_fsgid_changed() */
400
401 /*****************************************************************************/
402 /*
403  * search the process keyrings for the first matching key
404  * - we use the supplied match function to see if the description (or other
405  *   feature of interest) matches
406  * - we return -EAGAIN if we didn't find any matching key
407  * - we return -ENOKEY if we found only negative matching keys
408  */
409 key_ref_t search_process_keyrings(struct key_type *type,
410                                   const void *description,
411                                   key_match_func_t match,
412                                   struct task_struct *context)
413 {
414         struct request_key_auth *rka;
415         key_ref_t key_ref, ret, err;
416
417         might_sleep();
418
419         /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
420          * searchable, but we failed to find a key or we found a negative key;
421          * otherwise we want to return a sample error (probably -EACCES) if
422          * none of the keyrings were searchable
423          *
424          * in terms of priority: success > -ENOKEY > -EAGAIN > other error
425          */
426         key_ref = NULL;
427         ret = NULL;
428         err = ERR_PTR(-EAGAIN);
429
430         /* search the thread keyring first */
431         if (context->cred->thread_keyring) {
432                 key_ref = keyring_search_aux(
433                         make_key_ref(context->cred->thread_keyring, 1),
434                         context, type, description, match);
435                 if (!IS_ERR(key_ref))
436                         goto found;
437
438                 switch (PTR_ERR(key_ref)) {
439                 case -EAGAIN: /* no key */
440                         if (ret)
441                                 break;
442                 case -ENOKEY: /* negative key */
443                         ret = key_ref;
444                         break;
445                 default:
446                         err = key_ref;
447                         break;
448                 }
449         }
450
451         /* search the process keyring second */
452         if (context->signal->process_keyring) {
453                 key_ref = keyring_search_aux(
454                         make_key_ref(context->signal->process_keyring, 1),
455                         context, type, description, match);
456                 if (!IS_ERR(key_ref))
457                         goto found;
458
459                 switch (PTR_ERR(key_ref)) {
460                 case -EAGAIN: /* no key */
461                         if (ret)
462                                 break;
463                 case -ENOKEY: /* negative key */
464                         ret = key_ref;
465                         break;
466                 default:
467                         err = key_ref;
468                         break;
469                 }
470         }
471
472         /* search the session keyring */
473         if (context->signal->session_keyring) {
474                 rcu_read_lock();
475                 key_ref = keyring_search_aux(
476                         make_key_ref(rcu_dereference(
477                                              context->signal->session_keyring),
478                                      1),
479                         context, type, description, match);
480                 rcu_read_unlock();
481
482                 if (!IS_ERR(key_ref))
483                         goto found;
484
485                 switch (PTR_ERR(key_ref)) {
486                 case -EAGAIN: /* no key */
487                         if (ret)
488                                 break;
489                 case -ENOKEY: /* negative key */
490                         ret = key_ref;
491                         break;
492                 default:
493                         err = key_ref;
494                         break;
495                 }
496         }
497         /* or search the user-session keyring */
498         else if (context->cred->user->session_keyring) {
499                 key_ref = keyring_search_aux(
500                         make_key_ref(context->cred->user->session_keyring, 1),
501                         context, type, description, match);
502                 if (!IS_ERR(key_ref))
503                         goto found;
504
505                 switch (PTR_ERR(key_ref)) {
506                 case -EAGAIN: /* no key */
507                         if (ret)
508                                 break;
509                 case -ENOKEY: /* negative key */
510                         ret = key_ref;
511                         break;
512                 default:
513                         err = key_ref;
514                         break;
515                 }
516         }
517
518         /* if this process has an instantiation authorisation key, then we also
519          * search the keyrings of the process mentioned there
520          * - we don't permit access to request_key auth keys via this method
521          */
522         if (context->cred->request_key_auth &&
523             context == current &&
524             type != &key_type_request_key_auth
525             ) {
526                 /* defend against the auth key being revoked */
527                 down_read(&context->cred->request_key_auth->sem);
528
529                 if (key_validate(context->cred->request_key_auth) == 0) {
530                         rka = context->cred->request_key_auth->payload.data;
531
532                         key_ref = search_process_keyrings(type, description,
533                                                           match, rka->context);
534
535                         up_read(&context->cred->request_key_auth->sem);
536
537                         if (!IS_ERR(key_ref))
538                                 goto found;
539
540                         switch (PTR_ERR(key_ref)) {
541                         case -EAGAIN: /* no key */
542                                 if (ret)
543                                         break;
544                         case -ENOKEY: /* negative key */
545                                 ret = key_ref;
546                                 break;
547                         default:
548                                 err = key_ref;
549                                 break;
550                         }
551                 } else {
552                         up_read(&context->cred->request_key_auth->sem);
553                 }
554         }
555
556         /* no key - decide on the error we're going to go for */
557         key_ref = ret ? ret : err;
558
559 found:
560         return key_ref;
561
562 } /* end search_process_keyrings() */
563
564 /*****************************************************************************/
565 /*
566  * see if the key we're looking at is the target key
567  */
568 static int lookup_user_key_possessed(const struct key *key, const void *target)
569 {
570         return key == target;
571
572 } /* end lookup_user_key_possessed() */
573
574 /*****************************************************************************/
575 /*
576  * lookup a key given a key ID from userspace with a given permissions mask
577  * - don't create special keyrings unless so requested
578  * - partially constructed keys aren't found unless requested
579  */
580 key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
581                           key_perm_t perm)
582 {
583         struct request_key_auth *rka;
584         struct task_struct *t = current;
585         struct cred *cred = t->cred;
586         struct key *key;
587         key_ref_t key_ref, skey_ref;
588         int ret;
589
590         key_ref = ERR_PTR(-ENOKEY);
591
592         switch (id) {
593         case KEY_SPEC_THREAD_KEYRING:
594                 if (!cred->thread_keyring) {
595                         if (!create)
596                                 goto error;
597
598                         ret = install_thread_keyring();
599                         if (ret < 0) {
600                                 key = ERR_PTR(ret);
601                                 goto error;
602                         }
603                 }
604
605                 key = cred->thread_keyring;
606                 atomic_inc(&key->usage);
607                 key_ref = make_key_ref(key, 1);
608                 break;
609
610         case KEY_SPEC_PROCESS_KEYRING:
611                 if (!t->signal->process_keyring) {
612                         if (!create)
613                                 goto error;
614
615                         ret = install_process_keyring();
616                         if (ret < 0) {
617                                 key = ERR_PTR(ret);
618                                 goto error;
619                         }
620                 }
621
622                 key = t->signal->process_keyring;
623                 atomic_inc(&key->usage);
624                 key_ref = make_key_ref(key, 1);
625                 break;
626
627         case KEY_SPEC_SESSION_KEYRING:
628                 if (!t->signal->session_keyring) {
629                         /* always install a session keyring upon access if one
630                          * doesn't exist yet */
631                         ret = install_user_keyrings();
632                         if (ret < 0)
633                                 goto error;
634                         ret = install_session_keyring(
635                                 cred->user->session_keyring);
636                         if (ret < 0)
637                                 goto error;
638                 }
639
640                 rcu_read_lock();
641                 key = rcu_dereference(t->signal->session_keyring);
642                 atomic_inc(&key->usage);
643                 rcu_read_unlock();
644                 key_ref = make_key_ref(key, 1);
645                 break;
646
647         case KEY_SPEC_USER_KEYRING:
648                 if (!cred->user->uid_keyring) {
649                         ret = install_user_keyrings();
650                         if (ret < 0)
651                                 goto error;
652                 }
653
654                 key = cred->user->uid_keyring;
655                 atomic_inc(&key->usage);
656                 key_ref = make_key_ref(key, 1);
657                 break;
658
659         case KEY_SPEC_USER_SESSION_KEYRING:
660                 if (!cred->user->session_keyring) {
661                         ret = install_user_keyrings();
662                         if (ret < 0)
663                                 goto error;
664                 }
665
666                 key = cred->user->session_keyring;
667                 atomic_inc(&key->usage);
668                 key_ref = make_key_ref(key, 1);
669                 break;
670
671         case KEY_SPEC_GROUP_KEYRING:
672                 /* group keyrings are not yet supported */
673                 key = ERR_PTR(-EINVAL);
674                 goto error;
675
676         case KEY_SPEC_REQKEY_AUTH_KEY:
677                 key = cred->request_key_auth;
678                 if (!key)
679                         goto error;
680
681                 atomic_inc(&key->usage);
682                 key_ref = make_key_ref(key, 1);
683                 break;
684
685         case KEY_SPEC_REQUESTOR_KEYRING:
686                 if (!cred->request_key_auth)
687                         goto error;
688
689                 down_read(&cred->request_key_auth->sem);
690                 if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
691                         key_ref = ERR_PTR(-EKEYREVOKED);
692                         key = NULL;
693                 } else {
694                         rka = cred->request_key_auth->payload.data;
695                         key = rka->dest_keyring;
696                         atomic_inc(&key->usage);
697                 }
698                 up_read(&cred->request_key_auth->sem);
699                 if (!key)
700                         goto error;
701                 key_ref = make_key_ref(key, 1);
702                 break;
703
704         default:
705                 key_ref = ERR_PTR(-EINVAL);
706                 if (id < 1)
707                         goto error;
708
709                 key = key_lookup(id);
710                 if (IS_ERR(key)) {
711                         key_ref = ERR_CAST(key);
712                         goto error;
713                 }
714
715                 key_ref = make_key_ref(key, 0);
716
717                 /* check to see if we possess the key */
718                 skey_ref = search_process_keyrings(key->type, key,
719                                                    lookup_user_key_possessed,
720                                                    current);
721
722                 if (!IS_ERR(skey_ref)) {
723                         key_put(key);
724                         key_ref = skey_ref;
725                 }
726
727                 break;
728         }
729
730         if (!partial) {
731                 ret = wait_for_key_construction(key, true);
732                 switch (ret) {
733                 case -ERESTARTSYS:
734                         goto invalid_key;
735                 default:
736                         if (perm)
737                                 goto invalid_key;
738                 case 0:
739                         break;
740                 }
741         } else if (perm) {
742                 ret = key_validate(key);
743                 if (ret < 0)
744                         goto invalid_key;
745         }
746
747         ret = -EIO;
748         if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
749                 goto invalid_key;
750
751         /* check the permissions */
752         ret = key_task_permission(key_ref, t, perm);
753         if (ret < 0)
754                 goto invalid_key;
755
756 error:
757         return key_ref;
758
759 invalid_key:
760         key_ref_put(key_ref);
761         key_ref = ERR_PTR(ret);
762         goto error;
763
764 } /* end lookup_user_key() */
765
766 /*****************************************************************************/
767 /*
768  * join the named keyring as the session keyring if possible, or attempt to
769  * create a new one of that name if not
770  * - if the name is NULL, an empty anonymous keyring is installed instead
771  * - named session keyring joining is done with a semaphore held
772  */
773 long join_session_keyring(const char *name)
774 {
775         struct task_struct *tsk = current;
776         struct key *keyring;
777         long ret;
778
779         /* if no name is provided, install an anonymous keyring */
780         if (!name) {
781                 ret = install_session_keyring(NULL);
782                 if (ret < 0)
783                         goto error;
784
785                 rcu_read_lock();
786                 ret = rcu_dereference(tsk->signal->session_keyring)->serial;
787                 rcu_read_unlock();
788                 goto error;
789         }
790
791         /* allow the user to join or create a named keyring */
792         mutex_lock(&key_session_mutex);
793
794         /* look for an existing keyring of this name */
795         keyring = find_keyring_by_name(name, false);
796         if (PTR_ERR(keyring) == -ENOKEY) {
797                 /* not found - try and create a new one */
798                 keyring = keyring_alloc(name, tsk->cred->uid, tsk->cred->gid, tsk,
799                                         KEY_ALLOC_IN_QUOTA, NULL);
800                 if (IS_ERR(keyring)) {
801                         ret = PTR_ERR(keyring);
802                         goto error2;
803                 }
804         }
805         else if (IS_ERR(keyring)) {
806                 ret = PTR_ERR(keyring);
807                 goto error2;
808         }
809
810         /* we've got a keyring - now to install it */
811         ret = install_session_keyring(keyring);
812         if (ret < 0)
813                 goto error2;
814
815         ret = keyring->serial;
816         key_put(keyring);
817
818 error2:
819         mutex_unlock(&key_session_mutex);
820 error:
821         return ret;
822
823 } /* end join_session_keyring() */