cecfc9b99acf7d88d9f666271dc8dc0f02da7d82
[openafs.git] / src / afs / LINUX / osi_groups.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Implements:
12  * setgroups (syscall)
13  * setpag
14  *
15  */
16 #include <afsconfig.h>
17 #include "afs/param.h"
18 #ifdef LINUX_KEYRING_SUPPORT
19 #include <linux/seq_file.h>
20 #endif
21
22 RCSID
23     ("$Header$");
24
25 #include "afs/sysincludes.h"
26 #include "afsincludes.h"
27 #include "afs/afs_stats.h"      /* statistics */
28 #include "afs/nfsclient.h"
29 #ifdef AFS_LINUX22_ENV
30 #include "h/smp_lock.h"
31 #endif
32
33 #ifdef AFS_LINUX26_ONEGROUP_ENV
34 #define NUMPAGGROUPS 1
35 #else
36 #define NUMPAGGROUPS 2
37 #endif
38
39 #if defined(AFS_LINUX26_ENV)
40 static int
41 afs_setgroups(cred_t **cr, struct group_info *group_info, int change_parent)
42 {
43     struct group_info *old_info;
44
45     AFS_STATCNT(afs_setgroups);
46
47     old_info = (*cr)->cr_group_info;
48     get_group_info(group_info);
49     (*cr)->cr_group_info = group_info;
50     put_group_info(old_info);
51
52     crset(*cr);
53
54     if (change_parent) {
55         old_info = current->parent->group_info;
56         get_group_info(group_info);
57         current->parent->group_info = group_info;
58         put_group_info(old_info);
59     }
60
61     return (0);
62 }
63 #else
64 static int
65 afs_setgroups(cred_t **cr, int ngroups, gid_t * gidset, int change_parent)
66 {
67     int ngrps;
68     int i;
69     gid_t *gp;
70
71     AFS_STATCNT(afs_setgroups);
72
73     if (ngroups > NGROUPS)
74         return EINVAL;
75
76     gp = (*cr)->cr_groups;
77     if (ngroups < NGROUPS)
78         gp[ngroups] = (gid_t) NOGROUP;
79
80     for (i = ngroups; i > 0; i--) {
81         *gp++ = *gidset++;
82     }
83
84     (*cr)->cr_ngroups = ngroups;
85     crset(*cr);
86     return (0);
87 }
88 #endif
89
90 #if defined(AFS_LINUX26_ENV)
91 static struct group_info *
92 afs_getgroups(cred_t * cr)
93 {
94     AFS_STATCNT(afs_getgroups);
95
96     get_group_info(cr->cr_group_info);
97     return cr->cr_group_info;
98 }
99 #else
100 /* Returns number of groups. And we trust groups to be large enough to
101  * hold all the groups.
102  */
103 static int
104 afs_getgroups(cred_t *cr, gid_t *groups)
105 {
106     int i;
107     int n;
108     gid_t *gp;
109
110     AFS_STATCNT(afs_getgroups);
111
112     gp = cr->cr_groups;
113     n = cr->cr_ngroups;
114
115     for (i = 0; (i < n) && (*gp != (gid_t) NOGROUP); i++)
116         *groups++ = *gp++;
117     return i;
118 }
119 #endif
120
121 #if !defined(AFS_LINUX26_ENV)
122 /* Only propogate the PAG to the parent process. Unix's propogate to 
123  * all processes sharing the cred.
124  */
125 int
126 set_pag_in_parent(int pag, int g0, int g1)
127 {
128     int i;
129 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
130     gid_t *gp = current->parent->groups;
131     int ngroups = current->parent->ngroups;
132 #else
133     gid_t *gp = current->p_pptr->groups;
134     int ngroups = current->p_pptr->ngroups;
135 #endif
136
137     if ((ngroups < 2) || (afs_get_pag_from_groups(gp[0], gp[1]) == NOPAG)) {
138         /* We will have to shift grouplist to make room for pag */
139         if (ngroups + 2 > NGROUPS) {
140             return EINVAL;
141         }
142         for (i = ngroups - 1; i >= 0; i--) {
143             gp[i + 2] = gp[i];
144         }
145         ngroups += 2;
146     }
147     gp[0] = g0;
148     gp[1] = g1;
149     if (ngroups < NGROUPS)
150         gp[ngroups] = NOGROUP;
151
152 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
153     current->parent->ngroups = ngroups;
154 #else
155     current->p_pptr->ngroups = ngroups;
156 #endif
157     return 0;
158 }
159 #endif
160
161 #if defined(AFS_LINUX26_ENV)
162 int
163 __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
164          int change_parent)
165 {
166     struct group_info *group_info;
167     gid_t g0, g1;
168     struct group_info *tmp;
169     int i;
170 #ifdef AFS_LINUX26_ONEGROUP_ENV
171     int j;
172 #endif
173     int need_space = 0;
174
175     group_info = afs_getgroups(*cr);
176     if (group_info->ngroups < NUMPAGGROUPS
177         ||  afs_get_pag_from_groups(
178 #ifdef AFS_LINUX26_ONEGROUP_ENV
179             group_info
180 #else
181             GROUP_AT(group_info, 0) ,GROUP_AT(group_info, 1)
182 #endif
183                                     ) == NOPAG) 
184         /* We will have to make sure group_info is big enough for pag */
185         need_space = NUMPAGGROUPS;
186
187     tmp = groups_alloc(group_info->ngroups + need_space);
188     
189     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
190 #ifdef AFS_LINUX26_ONEGROUP_ENV
191     for (i = 0, j = 0; i < group_info->ngroups; ++i) {
192         int ths = GROUP_AT(group_info, i);
193         int last = i > 0 ? GROUP_AT(group_info, i-1) : 0;
194         if ((ths >> 24) == 'A')
195             continue;
196         if (last <= *newpag && ths > *newpag) {
197            GROUP_AT(tmp, j) = *newpag;
198            j++;
199         }
200         GROUP_AT(tmp, j) = ths;
201         j++;
202     }
203     if (j != i + need_space)
204         GROUP_AT(tmp, j) = *newpag;
205 #else
206     for (i = 0; i < group_info->ngroups; ++i)
207       GROUP_AT(tmp, i + need_space) = GROUP_AT(group_info, i);
208 #endif
209     put_group_info(group_info);
210     group_info = tmp;
211
212 #ifndef AFS_LINUX26_ONEGROUP_ENV
213     afs_get_groups_from_pag(*newpag, &g0, &g1);
214     GROUP_AT(group_info, 0) = g0;
215     GROUP_AT(group_info, 1) = g1;
216 #endif
217
218     afs_setgroups(cr, group_info, change_parent);
219
220     put_group_info(group_info);
221
222     return 0;
223 }
224
225 #ifdef LINUX_KEYRING_SUPPORT
226 #ifdef EXPORTS_KEY_TYPE_KEYRING
227 static struct key_type *__key_type_keyring = &key_type_keyring;
228 #else
229 static struct key_type *__key_type_keyring;
230 #endif
231
232 static int
233 install_session_keyring(struct task_struct *task, struct key *keyring)
234 {
235     struct key *old;
236     char desc[20];
237     unsigned long not_in_quota;
238     int code = -EINVAL;
239
240     if (!__key_type_keyring)
241         return code;
242
243     if (!keyring) {
244
245         /* create an empty session keyring */
246         not_in_quota = KEY_ALLOC_IN_QUOTA;
247         sprintf(desc, "_ses.%u", task->tgid);
248
249 #ifdef KEY_ALLOC_NEEDS_STRUCT_TASK
250         keyring = key_alloc(__key_type_keyring, desc,
251                             task->uid, task->gid, task,
252                             (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
253                             not_in_quota);
254 #else
255         keyring = key_alloc(__key_type_keyring, desc,
256                             task->uid, task->gid,
257                             (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
258                             not_in_quota);
259 #endif
260         if (IS_ERR(keyring)) {
261             code = PTR_ERR(keyring);
262             goto out;
263         }
264     }
265
266     code = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
267     if (code < 0) {
268         key_put(keyring);
269         goto out;
270     }
271
272     /* install the keyring */
273     spin_lock_irq(&task->sighand->siglock);
274     old = task->signal->session_keyring;
275     smp_wmb();
276     task->signal->session_keyring = keyring;
277     spin_unlock_irq(&task->sighand->siglock);
278
279     if (old)
280             key_put(old);
281
282 out:
283     return code;
284 }
285 #endif /* LINUX_KEYRING_SUPPORT */
286
287 #else
288 int
289 __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
290          int change_parent)
291 {
292     gid_t *gidset;
293     afs_int32 ngroups, code = 0;
294     int j;
295
296     gidset = (gid_t *) osi_Alloc(NGROUPS * sizeof(gidset[0]));
297     ngroups = afs_getgroups(*cr, gidset);
298
299     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
300         /* We will have to shift grouplist to make room for pag */
301         if (ngroups + 2 > NGROUPS) {
302             osi_Free((char *)gidset, NGROUPS * sizeof(int));
303             return EINVAL;
304         }
305         for (j = ngroups - 1; j >= 0; j--) {
306             gidset[j + 2] = gidset[j];
307         }
308         ngroups += 2;
309     }
310     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
311     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
312     code = afs_setgroups(cr, ngroups, gidset, change_parent);
313
314     /* If change_parent is set, then we should set the pag in the parent as
315      * well.
316      */
317     if (change_parent && !code) {
318         code = set_pag_in_parent(*newpag, gidset[0], gidset[1]);
319     }
320
321     osi_Free((char *)gidset, NGROUPS * sizeof(int));
322     return code;
323 }
324 #endif
325
326
327 int
328 setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
329        int change_parent)
330 {
331     int code;
332
333     AFS_STATCNT(setpag);
334
335     code = __setpag(cr, pagvalue, newpag, change_parent);
336
337 #ifdef LINUX_KEYRING_SUPPORT
338     if (code == 0 && (*cr)->cr_rgid != NFSXLATOR_CRED) {
339         (void) install_session_keyring(current, NULL);
340
341         if (current->signal->session_keyring) {
342             struct key *key;
343             key_perm_t perm;
344
345             perm = KEY_POS_VIEW | KEY_POS_SEARCH;
346             perm |= KEY_USR_VIEW | KEY_USR_SEARCH;
347
348 #ifdef KEY_ALLOC_NEEDS_STRUCT_TASK
349             key = key_alloc(&key_type_afs_pag, "_pag", 0, 0, current, perm, 1);
350 #else
351             key = key_alloc(&key_type_afs_pag, "_pag", 0, 0, perm, 1);
352 #endif
353
354             if (!IS_ERR(key)) {
355                 key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
356                                          current->signal->session_keyring, NULL);
357                 key_put(key);
358             }
359         }
360     }
361 #endif /* LINUX_KEYRING_SUPPORT */
362
363     return code;
364 }
365
366
367 /* Intercept the standard system call. */
368 extern asmlinkage long (*sys_setgroupsp) (int gidsetsize, gid_t * grouplist);
369 asmlinkage long
370 afs_xsetgroups(int gidsetsize, gid_t * grouplist)
371 {
372     long code;
373     cred_t *cr = crref();
374     afs_uint32 junk;
375     int old_pag;
376
377     lock_kernel();
378     old_pag = PagInCred(cr);
379     crfree(cr);
380     unlock_kernel();
381
382     code = (*sys_setgroupsp) (gidsetsize, grouplist);
383     if (code) {
384         return code;
385     }
386
387     lock_kernel();
388     cr = crref();
389     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
390         /* re-install old pag if there's room. */
391         code = __setpag(&cr, old_pag, &junk, 0);
392     }
393     crfree(cr);
394     unlock_kernel();
395
396     /* Linux syscall ABI returns errno as negative */
397     return (-code);
398 }
399
400 #if defined(AFS_LINUX24_ENV)
401 /* Intercept the standard uid32 system call. */
402 extern asmlinkage long (*sys_setgroups32p) (int gidsetsize, gid_t * grouplist);
403 asmlinkage long
404 afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
405 {
406     long code;
407     cred_t *cr = crref();
408     afs_uint32 junk;
409     int old_pag;
410
411     lock_kernel();
412     old_pag = PagInCred(cr);
413     crfree(cr);
414     unlock_kernel();
415
416     code = (*sys_setgroups32p) (gidsetsize, grouplist);
417
418     if (code) {
419         return code;
420     }
421
422     lock_kernel();
423     cr = crref();
424     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
425         /* re-install old pag if there's room. */
426         code = __setpag(&cr, old_pag, &junk, 0);
427     }
428     crfree(cr);
429     unlock_kernel();
430
431     /* Linux syscall ABI returns errno as negative */
432     return (-code);
433 }
434 #endif
435
436 #if defined(AFS_PPC64_LINUX20_ENV)
437 /* Intercept the uid16 system call as used by 32bit programs. */
438 extern long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
439 asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
440 {
441     long code;
442     cred_t *cr = crref();
443     afs_uint32 junk;
444     int old_pag;
445     
446     lock_kernel();
447     old_pag = PagInCred(cr);
448     crfree(cr);
449     unlock_kernel();
450     
451     code = (*sys32_setgroupsp)(gidsetsize, grouplist);
452     if (code) {
453         return code;
454     }
455     
456     lock_kernel();
457     cr = crref();
458     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
459         /* re-install old pag if there's room. */
460         code = __setpag(&cr, old_pag, &junk, 0);
461     }
462     crfree(cr);
463     unlock_kernel();
464     
465     /* Linux syscall ABI returns errno as negative */
466     return (-code);
467 }
468 #endif
469
470 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_AMD64_LINUX20_ENV)
471 /* Intercept the uid16 system call as used by 32bit programs. */
472 extern long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
473 asmlinkage long
474 afs32_xsetgroups(int gidsetsize, u16 * grouplist)
475 {
476     long code;
477     cred_t *cr = crref();
478     afs_uint32 junk;
479     int old_pag;
480     
481     lock_kernel();
482     old_pag = PagInCred(cr);
483     crfree(cr);
484     unlock_kernel();
485     
486     code = (*sys32_setgroupsp) (gidsetsize, grouplist);
487     if (code) {
488         return code;
489     }
490     
491     lock_kernel();
492     cr = crref();
493     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
494         /* re-install old pag if there's room. */
495         code = __setpag(&cr, old_pag, &junk, 0);
496     }
497     crfree(cr);
498     unlock_kernel();
499     
500     /* Linux syscall ABI returns errno as negative */
501     return (-code);
502 }
503
504 #ifdef AFS_LINUX24_ENV
505 /* Intercept the uid32 system call as used by 32bit programs. */
506 extern long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
507 asmlinkage long
508 afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
509 {
510     long code;
511     cred_t *cr = crref();
512     afs_uint32 junk;
513     int old_pag;
514
515     lock_kernel();
516     old_pag = PagInCred(cr);
517     crfree(cr);
518     unlock_kernel();
519
520     code = (*sys32_setgroups32p) (gidsetsize, grouplist);
521     if (code) {
522         return code;
523     }
524
525     lock_kernel();
526     cr = crref();
527     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
528         /* re-install old pag if there's room. */
529         code = __setpag(&cr, old_pag, &junk, 0);
530     }
531     crfree(cr);
532     unlock_kernel();
533
534     /* Linux syscall ABI returns errno as negative */
535     return (-code);
536 }
537 #endif
538 #endif
539
540
541 #ifdef LINUX_KEYRING_SUPPORT
542 static void afs_pag_describe(const struct key *key, struct seq_file *m)
543 {
544     seq_puts(m, key->description);
545
546     seq_printf(m, ": %u", key->datalen);
547 }
548
549 static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen)
550 {
551     int code;
552     afs_uint32 *userpag, pag = NOPAG;
553     int g0, g1;
554
555     if (key->uid != 0 || key->gid != 0)
556         return -EPERM;
557
558     code = -EINVAL;
559     get_group_info(current->group_info);
560
561     if (datalen != sizeof(afs_uint32) || !data)
562         goto error;
563
564     if (current->group_info->ngroups < NUMPAGGROUPS)
565         goto error;
566
567     /* ensure key being set matches current pag */
568 #ifdef AFS_LINUX26_ONEGROUP_ENV
569     pag = afs_get_pag_from_groups(current->group_info);
570 #else
571     g0 = GROUP_AT(current->group_info, 0);
572     g1 = GROUP_AT(current->group_info, 1);
573
574     pag = afs_get_pag_from_groups(g0, g1);
575 #endif
576     if (pag == NOPAG)
577         goto error;
578
579     userpag = (afs_uint32 *) data;
580     if (*userpag != pag)
581         goto error;
582
583     key->payload.value = (unsigned long) *userpag;
584     key->datalen = sizeof(afs_uint32);
585     code = 0;
586
587 error:
588     put_group_info(current->group_info);
589     return code;
590 }
591
592 static int afs_pag_match(const struct key *key, const void *description)
593 {
594         return strcmp(key->description, description) == 0;
595 }
596
597 static void afs_pag_destroy(struct key *key)
598 {
599     afs_uint32 pag = key->payload.value;
600     struct unixuser *pu;
601     int locked = ISAFS_GLOCK();
602
603     if (!locked)
604         AFS_GLOCK();
605     pu = afs_FindUser(pag, -1, READ_LOCK);
606     if (pu) {
607         pu->ct.EndTimestamp = 0;
608         pu->tokenTime = 0;
609         afs_PutUser(pu, READ_LOCK);
610     }
611     if (!locked)
612         AFS_GUNLOCK();
613 }
614
615 struct key_type key_type_afs_pag =
616 {
617     .name        = "afs_pag",
618     .describe    = afs_pag_describe,
619     .instantiate = afs_pag_instantiate,
620     .match       = afs_pag_match,
621     .destroy     = afs_pag_destroy,
622 };
623
624 #ifdef EXPORTED_TASKLIST_LOCK
625 extern rwlock_t tasklist_lock __attribute__((weak));
626 #endif
627
628 void osi_keyring_init(void)
629 {
630 #ifndef EXPORTS_KEY_TYPE_KEYRING
631     struct task_struct *p;
632 #ifdef EXPORTED_TASKLIST_LOCK
633     if (&tasklist_lock)
634       read_lock(&tasklist_lock);
635 #endif
636 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
637 #ifdef EXPORTED_TASKLIST_LOCK
638     else
639 #endif
640       rcu_read_lock();
641 #endif
642     p = find_task_by_pid(1);
643     if (p && p->user->session_keyring)
644         __key_type_keyring = p->user->session_keyring->type;
645 #ifdef EXPORTED_TASKLIST_LOCK
646     if (&tasklist_lock)
647        read_unlock(&tasklist_lock);
648 #endif
649 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
650 #ifdef EXPORTED_TASKLIST_LOCK
651     else
652 #endif
653       rcu_read_unlock();
654 #endif
655 #endif
656
657     register_key_type(&key_type_afs_pag);
658 }
659
660 void osi_keyring_shutdown(void)
661 {
662     unregister_key_type(&key_type_afs_pag);
663 }
664
665 #else
666 void osi_keyring_init(void)
667 {
668         return;
669 }
670
671 void osi_keyring_shutdown(void)
672 {
673         return;
674 }
675 #endif