venus: Remove dedebug
[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 "osi_compat.h"
28
29 #ifdef AFS_PAG_ONEGROUP_ENV
30
31 static afs_uint32
32 afs_linux_pag_from_groups(struct group_info *group_info) {
33     afs_uint32 g0 = 0;
34     afs_uint32 i;
35
36     if (group_info->ngroups < AFS_NUMPAGGROUPS)
37         return NOPAG;
38
39     for (i = 0; i < group_info->ngroups; i++) {
40         g0 = afs_from_kgid(GROUP_AT(group_info, i));
41         if (((g0 >> 24) & 0xff) == 'A')
42             return g0;
43     }
44     return NOPAG;
45 }
46
47 static inline void
48 afs_linux_pag_to_groups(afs_uint32 newpag,
49                         struct group_info *old, struct group_info **new) {
50     int need_space = 0;
51     int i;
52     int j;
53     afs_kgid_t newkgid = afs_make_kgid(newpag);
54
55     if (afs_linux_pag_from_groups(old) == NOPAG)
56         need_space = AFS_NUMPAGGROUPS;
57
58     *new = groups_alloc(old->ngroups + need_space);
59
60     for (i = 0, j = 0; i < old->ngroups; ++i) {
61         afs_kgid_t ths = GROUP_AT(old, i);
62         if ((afs_from_kgid(ths) >> 24) == 'A')
63             continue;
64         if ((i == 0 || !gid_lt(newkgid, GROUP_AT(old, i-1))) &&
65             gid_lt(newkgid, ths)) {
66            GROUP_AT(*new, j) = newkgid;
67            j++;
68         }
69         GROUP_AT(*new, j) = ths;
70         j++;
71     }
72     if (j != i + need_space)
73         GROUP_AT(*new, j) = newkgid;
74 }
75
76 #else
77
78 static inline afs_uint32
79 afs_linux_pag_from_groups(struct group_info *group_info) {
80
81     if (group_info->ngroups < AFS_NUMPAGGROUPS)
82         return NOPAG;
83
84     return afs_get_pag_from_groups(GROUP_AT(group_info, 0), GROUP_AT(group_info, 1));
85 }
86
87 static inline void
88 afs_linux_pag_to_groups(afs_uint32 newpag,
89                         struct group_info *old, struct group_info **new) {
90     int need_space = 0;
91     int i;
92     gid_t g0;
93     gid_t g1;
94
95     if (afs_linux_pag_from_groups(old) == NOPAG)
96         need_space = AFS_NUMPAGGROUPS;
97
98     *new = groups_alloc(old->ngroups + need_space);
99
100     for (i = 0; i < old->ngroups; ++i)
101           GROUP_AT(new, i + need_space) = GROUP_AT(old, i);
102
103     afs_get_groups_from_pag(newpag, &g0, g1);
104     GROUP_AT(new, 0) = g0;
105     GROUP_AT(new, 1) = g1;
106 }
107 #endif
108
109 afs_int32
110 osi_get_group_pag(afs_ucred_t *cred) {
111     return afs_linux_pag_from_groups(afs_cr_group_info(cred));
112 }
113
114
115 static int
116 afs_setgroups(cred_t **cr, struct group_info *group_info, int change_parent)
117 {
118     struct group_info *old_info;
119
120     AFS_STATCNT(afs_setgroups);
121
122     old_info = afs_cr_group_info(*cr);
123     get_group_info(group_info);
124     afs_set_cr_group_info(*cr, group_info);
125     put_group_info(old_info);
126
127     crset(*cr);
128
129 #if defined(STRUCT_TASK_STRUCT_HAS_PARENT) && !defined(STRUCT_TASK_STRUCT_HAS_CRED)
130     if (change_parent) {
131         old_info = current->parent->group_info;
132         get_group_info(group_info);
133         current->parent->group_info = group_info;
134         put_group_info(old_info);
135     }
136 #endif
137
138     return (0);
139 }
140
141 int
142 __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
143          int change_parent, struct group_info **old_groups)
144 {
145     struct group_info *group_info;
146     struct group_info *tmp;
147
148     get_group_info(afs_cr_group_info(*cr));
149     group_info = afs_cr_group_info(*cr);
150
151     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
152     afs_linux_pag_to_groups(*newpag, group_info, &tmp);
153
154     if (old_groups) {
155         *old_groups = group_info;
156     } else {
157         put_group_info(group_info);
158         group_info = NULL;
159     }
160
161     afs_setgroups(cr, tmp, change_parent);
162
163     put_group_info(tmp);
164
165     return 0;
166 }
167
168 #ifdef LINUX_KEYRING_SUPPORT
169 # if defined(EXPORTED_KEY_TYPE_KEYRING)
170 static struct key_type *__key_type_keyring = &key_type_keyring;
171 # else
172 static struct key_type *__key_type_keyring;
173 # endif
174
175 /* install_session_keyring returns negative error values */
176 static int
177 install_session_keyring(struct key *keyring)
178 {
179     struct key *old;
180     char desc[20];
181     int code = -EINVAL;
182     unsigned long flags;
183
184     if (!__key_type_keyring)
185         return code;
186
187     if (!keyring) {
188
189         /* create an empty session keyring */
190         sprintf(desc, "_ses.%u", current->tgid);
191
192         /* if we're root, don't count the keyring against our quota. This
193          * avoids starvation issues when dealing with PAM modules that always
194          * setpag() as root */
195         if (capable(CAP_SYS_ADMIN))
196             flags = KEY_ALLOC_NOT_IN_QUOTA;
197         else
198             flags = KEY_ALLOC_IN_QUOTA;
199
200         keyring = afs_linux_key_alloc(
201                             __key_type_keyring, desc,
202                             current_uid(), current_gid(),
203                             (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
204                             flags);
205
206         if (IS_ERR(keyring)) {
207             code = PTR_ERR(keyring);
208             goto out;
209         }
210     }
211
212     code = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
213     if (code < 0) {
214         key_put(keyring);
215         goto out;
216     }
217
218     /* install the keyring */
219     old = afs_set_session_keyring(keyring);
220     if (old)
221         key_put(old);
222
223 out:
224     return code;
225 }
226 #endif /* LINUX_KEYRING_SUPPORT */
227
228 /* Error codes from setpag must be positive, otherwise they don't
229  * make it back into userspace properly. Error codes from the
230  * Linux keyring utilities, and from install_session_keyring()
231  * are negative. So we need to be careful to convert them correctly
232  * here
233  */
234 int
235 setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
236        int change_parent)
237 {
238     int code;
239     struct group_info *old_groups = NULL;
240
241     AFS_STATCNT(setpag);
242
243     code = __setpag(cr, pagvalue, newpag, change_parent, &old_groups);
244
245 #ifdef LINUX_KEYRING_SUPPORT
246     if (code == 0 && afs_cr_rgid(*cr) != NFSXLATOR_CRED) {
247         code = install_session_keyring(NULL);
248         if (code == 0 && current_session_keyring()) {
249             struct key *key;
250             key_perm_t perm;
251
252             perm = KEY_POS_VIEW | KEY_POS_SEARCH;
253             perm |= KEY_USR_VIEW | KEY_USR_SEARCH;
254
255             key = afs_linux_key_alloc(&key_type_afs_pag, "_pag", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, perm, KEY_ALLOC_NOT_IN_QUOTA);
256
257             if (!IS_ERR(key)) {
258                 code = key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
259                                          current_session_keyring(), NULL);
260                 key_put(key);
261             } else {
262                 code = PTR_ERR(key);
263             }
264         }
265         if (code)
266             code = -code;
267     }
268 #endif /* LINUX_KEYRING_SUPPORT */
269
270     if (code) {
271         if (old_groups) {
272             afs_setgroups(cr, old_groups, change_parent);
273             put_group_info(old_groups);
274             old_groups = NULL;
275         }
276         if (*newpag > -1) {
277             afs_MarkUserExpired(*newpag);
278             *newpag = -1;
279         }
280     }
281
282     return code;
283 }
284
285 #ifndef LINUX_KEYRING_SUPPORT
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     old_pag = PagInCred(cr);
297     crfree(cr);
298
299     code = (*sys_setgroupsp) (gidsetsize, grouplist);
300     if (code) {
301         return code;
302     }
303
304     cr = crref();
305     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
306         /* re-install old pag if there's room. */
307         code = __setpag(&cr, old_pag, &junk, 0, NULL);
308     }
309     crfree(cr);
310
311     /* Linux syscall ABI returns errno as negative */
312     return (-code);
313 }
314
315 /* Intercept the standard uid32 system call. */
316 extern asmlinkage int (*sys_setgroups32p) (int gidsetsize,
317                                            __kernel_gid32_t * grouplist);
318 asmlinkage long
319 afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
320 {
321     long code;
322     cred_t *cr = crref();
323     afs_uint32 junk;
324     int old_pag;
325
326     old_pag = PagInCred(cr);
327     crfree(cr);
328
329     code = (*sys_setgroups32p) (gidsetsize, grouplist);
330
331     if (code) {
332         return code;
333     }
334
335     cr = crref();
336     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
337         /* re-install old pag if there's room. */
338         code = __setpag(&cr, old_pag, &junk, 0, NULL);
339     }
340     crfree(cr);
341
342     /* Linux syscall ABI returns errno as negative */
343     return (-code);
344 }
345
346 # if defined(AFS_PPC64_LINUX_ENV)
347 /* Intercept the uid16 system call as used by 32bit programs. */
348 extern asmlinkage long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
349 asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
350 {
351     long code;
352     cred_t *cr = crref();
353     afs_uint32 junk;
354     int old_pag;
355     
356     old_pag = PagInCred(cr);
357     crfree(cr);
358     
359     code = (*sys32_setgroupsp)(gidsetsize, grouplist);
360     if (code) {
361         return code;
362     }
363     
364     cr = crref();
365     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
366         /* re-install old pag if there's room. */
367         code = __setpag(&cr, old_pag, &junk, 0, NULL);
368     }
369     crfree(cr);
370     
371     /* Linux syscall ABI returns errno as negative */
372     return (-code);
373 }
374 # endif
375
376 # if defined(AFS_SPARC64_LINUX_ENV) || defined(AFS_AMD64_LINUX_ENV)
377 /* Intercept the uid16 system call as used by 32bit programs. */
378 #  ifdef AFS_AMD64_LINUX_ENV
379 extern asmlinkage long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
380 #  endif /* AFS_AMD64_LINUX_ENV */
381 #  ifdef AFS_SPARC64_LINUX_ENV
382 extern asmlinkage int (*sys32_setgroupsp) (int gidsetsize,
383                                            __kernel_gid32_t * grouplist);
384 #  endif /* AFS_SPARC64_LINUX_ENV */
385 asmlinkage long
386 afs32_xsetgroups(int gidsetsize, u16 * grouplist)
387 {
388     long code;
389     cred_t *cr = crref();
390     afs_uint32 junk;
391     int old_pag;
392     
393     old_pag = PagInCred(cr);
394     crfree(cr);
395     
396     code = (*sys32_setgroupsp) (gidsetsize, grouplist);
397     if (code) {
398         return code;
399     }
400     
401     cr = crref();
402     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
403         /* re-install old pag if there's room. */
404         code = __setpag(&cr, old_pag, &junk, 0, NULL);
405     }
406     crfree(cr);
407     
408     /* Linux syscall ABI returns errno as negative */
409     return (-code);
410 }
411
412 /* Intercept the uid32 system call as used by 32bit programs. */
413 #  ifdef AFS_AMD64_LINUX_ENV
414 extern asmlinkage long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
415 #  endif /* AFS_AMD64_LINUX_ENV */
416 #  ifdef AFS_SPARC64_LINUX_ENV
417 extern asmlinkage int (*sys32_setgroups32p) (int gidsetsize,
418                                              __kernel_gid32_t * grouplist);
419 #  endif /* AFS_SPARC64_LINUX_ENV */
420 asmlinkage long
421 afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
422 {
423     long code;
424     cred_t *cr = crref();
425     afs_uint32 junk;
426     int old_pag;
427
428     old_pag = PagInCred(cr);
429     crfree(cr);
430
431     code = (*sys32_setgroups32p) (gidsetsize, grouplist);
432     if (code) {
433         return code;
434     }
435
436     cr = crref();
437     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
438         /* re-install old pag if there's room. */
439         code = __setpag(&cr, old_pag, &junk, 0, NULL);
440     }
441     crfree(cr);
442
443     /* Linux syscall ABI returns errno as negative */
444     return (-code);
445 }
446 # endif
447 #endif /* !LINUX_KEYRING_SUPPORT */
448
449 #ifdef LINUX_KEYRING_SUPPORT
450 static void afs_pag_describe(const struct key *key, struct seq_file *m)
451 {
452     seq_puts(m, key->description);
453
454     seq_printf(m, ": %u", key->datalen);
455 }
456
457 #if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
458 static int afs_pag_instantiate(struct key *key, struct key_preparsed_payload *prep)
459 #else
460 static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen)
461 #endif
462 {
463     int code;
464     afs_uint32 *userpag, pag = NOPAG;
465
466     if (!uid_eq(key->uid, GLOBAL_ROOT_UID) || !gid_eq(key->gid, GLOBAL_ROOT_GID))
467         return -EPERM;
468
469     code = -EINVAL;
470     get_group_info(current_group_info());
471
472 #if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
473     if (prep->datalen != sizeof(afs_uint32) || !prep->data)
474 #else
475     if (datalen != sizeof(afs_uint32) || !data)
476 #endif
477         goto error;
478
479     /* ensure key being set matches current pag */
480     pag = afs_linux_pag_from_groups(current_group_info());
481
482     if (pag == NOPAG)
483         goto error;
484
485 #if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
486     userpag = (afs_uint32 *)prep->data;
487 #else
488     userpag = (afs_uint32 *)data;
489 #endif
490     if (*userpag != pag)
491         goto error;
492
493 #if defined(STRUCT_KEY_HAS_PAYLOAD_VALUE)
494     key->payload.value = (unsigned long) *userpag;
495 #else
496     memcpy(&key->payload, userpag, sizeof(afs_uint32));
497 #endif
498     key->datalen = sizeof(afs_uint32);
499     code = 0;
500
501 error:
502     put_group_info(current_group_info());
503     return code;
504 }
505
506 #if !defined(STRUCT_KEY_TYPE_HAS_MATCH_PREPARSE)
507 /* Note that we only define a ->match function if struct
508  * key_type.match_preparse does _not_ exist. If key_type.match_preparse does
509  * exist, we would use that to specify an alternative comparison function; but
510  * since we just rely on default behavior, we don't need to actually specify
511  * one. But for kernels with no such match_preparse function, we need to
512  * specify a 'match' function, since there is no default. */
513 static int afs_pag_match(const struct key *key, const void *description)
514 {
515         return strcmp(key->description, description) == 0;
516 }
517 #endif
518
519 static void afs_pag_destroy(struct key *key)
520 {
521     afs_uint32 pag;
522     int locked = ISAFS_GLOCK();
523
524 #if defined(STRUCT_KEY_HAS_PAYLOAD_VALUE)
525     pag = key->payload.value;
526 #else
527     memcpy(&pag, &key->payload, sizeof(afs_uint32));
528 #endif
529
530     if (!locked)
531         AFS_GLOCK();
532
533     afs_MarkUserExpired(pag);
534
535     if (!locked)
536         AFS_GUNLOCK();
537 }
538
539 struct key_type key_type_afs_pag =
540 {
541     .name        = "afs_pag",
542     .describe    = afs_pag_describe,
543 #if defined(STRUCT_KEY_TYPE_HAS_INSTANTIATE_PREP)
544     .instantiate_prep = afs_pag_instantiate,
545     .instantiate = NULL,
546 #else
547     .instantiate = afs_pag_instantiate,
548 #endif
549 #if !defined(STRUCT_KEY_TYPE_HAS_MATCH_PREPARSE)
550     .match       = afs_pag_match,
551 #endif
552     .destroy     = afs_pag_destroy,
553 };
554
555 #ifdef EXPORTED_TASKLIST_LOCK
556 extern rwlock_t tasklist_lock __attribute__((weak));
557 #endif
558
559 void osi_keyring_init(void)
560 {
561 #if !defined(EXPORTED_KEY_TYPE_KEYRING)
562     struct task_struct *p;
563
564     /* If we can't lock the tasklist, either with its explicit lock,
565      * or by using the RCU lock, then we can't safely work out the 
566      * type of a keyring. So, we have to rely on the weak reference. 
567      * If that's not available, then keyring based PAGs won't work.
568      */
569     
570 #if defined(EXPORTED_TASKLIST_LOCK) || defined(HAVE_LINUX_RCU_READ_LOCK)
571     if (__key_type_keyring == NULL) {
572 # ifdef EXPORTED_TASKLIST_LOCK
573         if (&tasklist_lock)
574             read_lock(&tasklist_lock);
575 # endif
576 # if defined(HAVE_LINUX_RCU_READ_LOCK)
577 #  ifdef EXPORTED_TASKLIST_LOCK
578         else
579 #  endif
580             rcu_read_lock();
581 # endif
582 #if defined(HAVE_LINUX_FIND_TASK_BY_PID)
583         p = find_task_by_pid(1);
584 #else
585         p = find_task_by_vpid(1);
586 #endif
587         if (p && task_user(p)->session_keyring)
588             __key_type_keyring = task_user(p)->session_keyring->type;
589 # ifdef EXPORTED_TASKLIST_LOCK
590         if (&tasklist_lock)
591             read_unlock(&tasklist_lock);
592 # endif
593 # if defined(HAVE_LINUX_RCU_READ_LOCK)
594 #  ifdef EXPORTED_TASKLIST_LOCK
595         else
596 #  endif
597             rcu_read_unlock();
598 # endif
599     }
600 #endif
601 #endif
602
603     register_key_type(&key_type_afs_pag);
604 }
605
606 void osi_keyring_shutdown(void)
607 {
608     unregister_key_type(&key_type_afs_pag);
609 }
610
611 afs_int32
612 osi_get_keyring_pag(afs_ucred_t *cred)
613 {
614     struct key *key;
615     afs_uint32 newpag;
616     afs_int32 keyring_pag = NOPAG;
617
618     if (afs_cr_rgid(cred) != NFSXLATOR_CRED) {
619         key = afs_linux_search_keyring(cred, &key_type_afs_pag);
620
621         if (!IS_ERR(key)) {
622             if (key_validate(key) == 0 && uid_eq(key->uid, GLOBAL_ROOT_UID)) {      /* also verify in the session keyring? */
623 #if defined(STRUCT_KEY_HAS_PAYLOAD_VALUE)
624                 keyring_pag = key->payload.value;
625 #else
626                 memcpy(&keyring_pag, &key->payload, sizeof(afs_int32));
627 #endif
628                 /* Only set PAG in groups if needed,
629                  * and the creds are from the current process */
630                 if (afs_linux_cred_is_current(cred) &&
631                     ((keyring_pag >> 24) & 0xff) == 'A' &&
632                     keyring_pag != afs_linux_pag_from_groups(current_group_info())) {
633                         __setpag(&cred, keyring_pag, &newpag, 0, NULL);
634                 }
635             }
636             key_put(key);
637         }
638     }
639     return keyring_pag;
640 }
641
642 #else
643 void osi_keyring_init(void)
644 {
645         return;
646 }
647
648 void osi_keyring_shutdown(void)
649 {
650         return;
651 }
652 #endif