DEVEL15-linux-keyring-pag-update-20060817
[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 #include <asm/unistd.h>
193 #include <linux/keyctl.h>
194
195 static int errno;
196 static inline _syscall2(long, keyctl, int, option, void*, arg2);
197
198 static long
199 __join_session_keyring(char *name)
200 {
201         return keyctl(KEYCTL_JOIN_SESSION_KEYRING, name);
202 }
203 #endif /* LINUX_KEYRING_SUPPORT */
204
205 #else
206 int
207 __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
208          int change_parent)
209 {
210     gid_t *gidset;
211     afs_int32 ngroups, code = 0;
212     int j;
213
214     gidset = (gid_t *) osi_Alloc(NGROUPS * sizeof(gidset[0]));
215     ngroups = afs_getgroups(*cr, gidset);
216
217     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
218         /* We will have to shift grouplist to make room for pag */
219         if (ngroups + 2 > NGROUPS) {
220             osi_Free((char *)gidset, NGROUPS * sizeof(int));
221             return EINVAL;
222         }
223         for (j = ngroups - 1; j >= 0; j--) {
224             gidset[j + 2] = gidset[j];
225         }
226         ngroups += 2;
227     }
228     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
229     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
230     code = afs_setgroups(cr, ngroups, gidset, change_parent);
231
232     /* If change_parent is set, then we should set the pag in the parent as
233      * well.
234      */
235     if (change_parent && !code) {
236         code = set_pag_in_parent(*newpag, gidset[0], gidset[1]);
237     }
238
239     osi_Free((char *)gidset, NGROUPS * sizeof(int));
240     return code;
241 }
242 #endif
243
244
245 int
246 setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
247        int change_parent)
248 {
249     int code;
250
251     AFS_STATCNT(setpag);
252
253     code = __setpag(cr, pagvalue, newpag, change_parent);
254
255 #ifdef LINUX_KEYRING_SUPPORT
256     if (code == 0) {
257
258         (void) __join_session_keyring(NULL);
259
260         if (current->signal->session_keyring) {
261             struct key *key;
262             key_perm_t perm;
263
264             perm = KEY_POS_VIEW | KEY_POS_SEARCH;
265             perm |= KEY_USR_VIEW | KEY_USR_SEARCH;
266
267 #ifdef KEY_ALLOC_NEEDS_STRUCT_TASK
268             key = key_alloc(&key_type_afs_pag, "_pag", 0, 0, current, perm, 1);
269 #else
270             key = key_alloc(&key_type_afs_pag, "_pag", 0, 0, perm, 1);
271 #endif
272
273             if (!IS_ERR(key)) {
274                 key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
275                                          current->signal->session_keyring, NULL);
276                 key_put(key);
277             }
278         }
279     }
280 #endif /* LINUX_KEYRING_SUPPORT */
281
282     return code;
283 }
284
285
286 /* Intercept the standard system call. */
287 extern asmlinkage long (*sys_setgroupsp) (int gidsetsize, gid_t * grouplist);
288 asmlinkage long
289 afs_xsetgroups(int gidsetsize, gid_t * grouplist)
290 {
291     long code;
292     cred_t *cr = crref();
293     afs_uint32 junk;
294     int old_pag;
295
296     lock_kernel();
297     old_pag = PagInCred(cr);
298     crfree(cr);
299     unlock_kernel();
300
301     code = (*sys_setgroupsp) (gidsetsize, grouplist);
302     if (code) {
303         return code;
304     }
305
306     lock_kernel();
307     cr = crref();
308     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
309         /* re-install old pag if there's room. */
310         code = __setpag(&cr, old_pag, &junk, 0);
311     }
312     crfree(cr);
313     unlock_kernel();
314
315     /* Linux syscall ABI returns errno as negative */
316     return (-code);
317 }
318
319 #if defined(AFS_LINUX24_ENV)
320 /* Intercept the standard uid32 system call. */
321 extern asmlinkage long (*sys_setgroups32p) (int gidsetsize, gid_t * grouplist);
322 asmlinkage long
323 afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
324 {
325     long code;
326     cred_t *cr = crref();
327     afs_uint32 junk;
328     int old_pag;
329
330     lock_kernel();
331     old_pag = PagInCred(cr);
332     crfree(cr);
333     unlock_kernel();
334
335     code = (*sys_setgroups32p) (gidsetsize, grouplist);
336
337     if (code) {
338         return code;
339     }
340
341     lock_kernel();
342     cr = crref();
343     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
344         /* re-install old pag if there's room. */
345         code = __setpag(&cr, old_pag, &junk, 0);
346     }
347     crfree(cr);
348     unlock_kernel();
349
350     /* Linux syscall ABI returns errno as negative */
351     return (-code);
352 }
353 #endif
354
355 #if defined(AFS_PPC64_LINUX20_ENV)
356 /* Intercept the uid16 system call as used by 32bit programs. */
357 extern long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
358 asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
359 {
360     long code;
361     cred_t *cr = crref();
362     afs_uint32 junk;
363     int old_pag;
364     
365     lock_kernel();
366     old_pag = PagInCred(cr);
367     crfree(cr);
368     unlock_kernel();
369     
370     code = (*sys32_setgroupsp)(gidsetsize, grouplist);
371     if (code) {
372         return code;
373     }
374     
375     lock_kernel();
376     cr = crref();
377     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
378         /* re-install old pag if there's room. */
379         code = __setpag(&cr, old_pag, &junk, 0);
380     }
381     crfree(cr);
382     unlock_kernel();
383     
384     /* Linux syscall ABI returns errno as negative */
385     return (-code);
386 }
387 #endif
388
389 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_AMD64_LINUX20_ENV)
390 /* Intercept the uid16 system call as used by 32bit programs. */
391 extern long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
392 asmlinkage long
393 afs32_xsetgroups(int gidsetsize, u16 * grouplist)
394 {
395     long code;
396     cred_t *cr = crref();
397     afs_uint32 junk;
398     int old_pag;
399     
400     lock_kernel();
401     old_pag = PagInCred(cr);
402     crfree(cr);
403     unlock_kernel();
404     
405     code = (*sys32_setgroupsp) (gidsetsize, grouplist);
406     if (code) {
407         return code;
408     }
409     
410     lock_kernel();
411     cr = crref();
412     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
413         /* re-install old pag if there's room. */
414         code = __setpag(&cr, old_pag, &junk, 0);
415     }
416     crfree(cr);
417     unlock_kernel();
418     
419     /* Linux syscall ABI returns errno as negative */
420     return (-code);
421 }
422
423 #ifdef AFS_LINUX24_ENV
424 /* Intercept the uid32 system call as used by 32bit programs. */
425 extern long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
426 asmlinkage long
427 afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
428 {
429     long code;
430     cred_t *cr = crref();
431     afs_uint32 junk;
432     int old_pag;
433
434     lock_kernel();
435     old_pag = PagInCred(cr);
436     crfree(cr);
437     unlock_kernel();
438
439     code = (*sys32_setgroups32p) (gidsetsize, grouplist);
440     if (code) {
441         return code;
442     }
443
444     lock_kernel();
445     cr = crref();
446     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
447         /* re-install old pag if there's room. */
448         code = __setpag(&cr, old_pag, &junk, 0);
449     }
450     crfree(cr);
451     unlock_kernel();
452
453     /* Linux syscall ABI returns errno as negative */
454     return (-code);
455 }
456 #endif
457 #endif
458
459
460 #ifdef LINUX_KEYRING_SUPPORT
461 static void afs_pag_describe(const struct key *key, struct seq_file *m)
462 {
463     seq_puts(m, key->description);
464
465     seq_printf(m, ": %u", key->datalen);
466 }
467
468 static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen)
469 {
470     int code;
471     afs_uint32 *userpag, pag = NOPAG;
472     int g0, g1;
473
474     if (key->uid != 0 || key->gid != 0)
475         return -EPERM;
476
477     code = -EINVAL;
478     get_group_info(current->group_info);
479
480     if (datalen != sizeof(afs_uint32) || !data)
481         goto error;
482
483     if (current->group_info->ngroups < 2)
484         goto error;
485
486     /* ensure key being set matches current pag */
487
488     g0 = GROUP_AT(current->group_info, 0);
489     g1 = GROUP_AT(current->group_info, 1);
490
491     pag = afs_get_pag_from_groups(g0, g1);
492     if (pag == NOPAG)
493         goto error;
494
495     userpag = (afs_uint32 *) data;
496     if (*userpag != pag)
497         goto error;
498
499     key->payload.value = (unsigned long) *userpag;
500     key->datalen = sizeof(afs_uint32);
501     code = 0;
502
503 error:
504     put_group_info(current->group_info);
505     return code;
506 }
507
508 static int afs_pag_match(const struct key *key, const void *description)
509 {
510         return strcmp(key->description, description) == 0;
511 }
512
513 struct key_type key_type_afs_pag =
514 {
515     .name        = "afs_pag",
516     .describe    = afs_pag_describe,
517     .instantiate = afs_pag_instantiate,
518     .match       = afs_pag_match,
519 };
520
521 void osi_keyring_init(void)
522 {
523     register_key_type(&key_type_afs_pag);
524 }
525
526 void osi_keyring_shutdown(void)
527 {
528     unregister_key_type(&key_type_afs_pag);
529 }
530
531 #else
532 void osi_keyring_init(void)
533 {
534         return;
535 }
536
537 void osi_keyring_shutdown(void)
538 {
539         return;
540 }
541 #endif