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