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