2 * Copyright 2000, International Business Machines Corporation and others.
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
16 #include <afsconfig.h>
17 #include "afs/param.h"
18 #ifdef LINUX_KEYRING_SUPPORT
19 #include <linux/seq_file.h>
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"
29 #ifdef AFS_PAG_ONEGROUP_ENV
32 afs_linux_pag_from_groups(struct group_info
*group_info
) {
36 if (group_info
->ngroups
< AFS_NUMPAGGROUPS
)
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')
48 afs_linux_pag_to_groups(afs_uint32 newpag
,
49 struct group_info
*old
, struct group_info
**new) {
53 afs_kgid_t newkgid
= afs_make_kgid(newpag
);
55 if (afs_linux_pag_from_groups(old
) == NOPAG
)
56 need_space
= AFS_NUMPAGGROUPS
;
58 *new = groups_alloc(old
->ngroups
+ need_space
);
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')
64 if ((i
== 0 || !gid_lt(newkgid
, GROUP_AT(old
, i
-1))) &&
65 gid_lt(newkgid
, ths
)) {
66 GROUP_AT(*new, j
) = newkgid
;
69 GROUP_AT(*new, j
) = ths
;
72 if (j
!= i
+ need_space
)
73 GROUP_AT(*new, j
) = newkgid
;
78 static inline afs_uint32
79 afs_linux_pag_from_groups(struct group_info
*group_info
) {
81 if (group_info
->ngroups
< AFS_NUMPAGGROUPS
)
84 return afs_get_pag_from_groups(GROUP_AT(group_info
, 0), GROUP_AT(group_info
, 1));
88 afs_linux_pag_to_groups(afs_uint32 newpag
,
89 struct group_info
*old
, struct group_info
**new) {
95 if (afs_linux_pag_from_groups(old
) == NOPAG
)
96 need_space
= AFS_NUMPAGGROUPS
;
98 *new = groups_alloc(old
->ngroups
+ need_space
);
100 for (i
= 0; i
< old
->ngroups
; ++i
)
101 GROUP_AT(new, i
+ need_space
) = GROUP_AT(old
, i
);
103 afs_get_groups_from_pag(newpag
, &g0
, g1
);
104 GROUP_AT(new, 0) = g0
;
105 GROUP_AT(new, 1) = g1
;
110 osi_get_group_pag(afs_ucred_t
*cred
) {
111 return afs_linux_pag_from_groups(afs_cr_group_info(cred
));
116 afs_setgroups(cred_t
**cr
, struct group_info
*group_info
, int change_parent
)
118 struct group_info
*old_info
;
120 AFS_STATCNT(afs_setgroups
);
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
);
129 #if defined(STRUCT_TASK_STRUCT_HAS_PARENT) && !defined(STRUCT_TASK_STRUCT_HAS_CRED)
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
);
142 __setpag(cred_t
**cr
, afs_uint32 pagvalue
, afs_uint32
*newpag
,
143 int change_parent
, struct group_info
**old_groups
)
145 struct group_info
*group_info
;
146 struct group_info
*tmp
;
148 get_group_info(afs_cr_group_info(*cr
));
149 group_info
= afs_cr_group_info(*cr
);
151 *newpag
= (pagvalue
== -1 ? genpag() : pagvalue
);
152 afs_linux_pag_to_groups(*newpag
, group_info
, &tmp
);
155 *old_groups
= group_info
;
157 put_group_info(group_info
);
161 afs_setgroups(cr
, tmp
, change_parent
);
168 #ifdef LINUX_KEYRING_SUPPORT
169 extern struct key_type key_type_keyring
__attribute__((weak
));
170 static struct key_type
*__key_type_keyring
= &key_type_keyring
;
172 /* install_session_keyring returns negative error values */
174 install_session_keyring(struct key
*keyring
)
181 if (!__key_type_keyring
)
186 /* create an empty session keyring */
187 sprintf(desc
, "_ses.%u", current
->tgid
);
189 /* if we're root, don't count the keyring against our quota. This
190 * avoids starvation issues when dealing with PAM modules that always
191 * setpag() as root */
192 if (capable(CAP_SYS_ADMIN
))
193 flags
= KEY_ALLOC_NOT_IN_QUOTA
;
195 flags
= KEY_ALLOC_IN_QUOTA
;
197 keyring
= afs_linux_key_alloc(
198 __key_type_keyring
, desc
,
199 current_uid(), current_gid(),
200 (KEY_POS_ALL
& ~KEY_POS_SETATTR
) | KEY_USR_ALL
,
203 if (IS_ERR(keyring
)) {
204 code
= PTR_ERR(keyring
);
209 code
= key_instantiate_and_link(keyring
, NULL
, 0, NULL
, NULL
);
215 /* install the keyring */
216 old
= afs_set_session_keyring(keyring
);
223 #endif /* LINUX_KEYRING_SUPPORT */
225 /* Error codes from setpag must be positive, otherwise they don't
226 * make it back into userspace properly. Error codes from the
227 * Linux keyring utilities, and from install_session_keyring()
228 * are negative. So we need to be careful to convert them correctly
232 setpag(cred_t
**cr
, afs_uint32 pagvalue
, afs_uint32
*newpag
,
236 struct group_info
*old_groups
= NULL
;
240 code
= __setpag(cr
, pagvalue
, newpag
, change_parent
, &old_groups
);
242 #ifdef LINUX_KEYRING_SUPPORT
243 if (code
== 0 && afs_cr_rgid(*cr
) != NFSXLATOR_CRED
) {
244 code
= install_session_keyring(NULL
);
245 if (code
== 0 && current_session_keyring()) {
249 perm
= KEY_POS_VIEW
| KEY_POS_SEARCH
;
250 perm
|= KEY_USR_VIEW
| KEY_USR_SEARCH
;
252 key
= afs_linux_key_alloc(&key_type_afs_pag
, "_pag", GLOBAL_ROOT_UID
, GLOBAL_ROOT_GID
, perm
, KEY_ALLOC_NOT_IN_QUOTA
);
255 code
= key_instantiate_and_link(key
, (void *) newpag
, sizeof(afs_uint32
),
256 current_session_keyring(), NULL
);
265 #endif /* LINUX_KEYRING_SUPPORT */
269 afs_setgroups(cr
, old_groups
, change_parent
);
270 put_group_info(old_groups
);
274 afs_MarkUserExpired(*newpag
);
282 #ifndef LINUX_KEYRING_SUPPORT
283 /* Intercept the standard system call. */
284 extern asmlinkage
long (*sys_setgroupsp
) (int gidsetsize
, gid_t
* grouplist
);
286 afs_xsetgroups(int gidsetsize
, gid_t
* grouplist
)
289 cred_t
*cr
= crref();
293 old_pag
= PagInCred(cr
);
296 code
= (*sys_setgroupsp
) (gidsetsize
, grouplist
);
302 if (old_pag
!= NOPAG
&& PagInCred(cr
) == NOPAG
) {
303 /* re-install old pag if there's room. */
304 code
= __setpag(&cr
, old_pag
, &junk
, 0, NULL
);
308 /* Linux syscall ABI returns errno as negative */
312 /* Intercept the standard uid32 system call. */
313 extern asmlinkage
int (*sys_setgroups32p
) (int gidsetsize
,
314 __kernel_gid32_t
* grouplist
);
316 afs_xsetgroups32(int gidsetsize
, gid_t
* grouplist
)
319 cred_t
*cr
= crref();
323 old_pag
= PagInCred(cr
);
326 code
= (*sys_setgroups32p
) (gidsetsize
, grouplist
);
333 if (old_pag
!= NOPAG
&& PagInCred(cr
) == NOPAG
) {
334 /* re-install old pag if there's room. */
335 code
= __setpag(&cr
, old_pag
, &junk
, 0, NULL
);
339 /* Linux syscall ABI returns errno as negative */
343 # if defined(AFS_PPC64_LINUX20_ENV)
344 /* Intercept the uid16 system call as used by 32bit programs. */
345 extern asmlinkage
long (*sys32_setgroupsp
)(int gidsetsize
, gid_t
*grouplist
);
346 asmlinkage
long afs32_xsetgroups(int gidsetsize
, gid_t
*grouplist
)
349 cred_t
*cr
= crref();
353 old_pag
= PagInCred(cr
);
356 code
= (*sys32_setgroupsp
)(gidsetsize
, grouplist
);
362 if (old_pag
!= NOPAG
&& PagInCred(cr
) == NOPAG
) {
363 /* re-install old pag if there's room. */
364 code
= __setpag(&cr
, old_pag
, &junk
, 0, NULL
);
368 /* Linux syscall ABI returns errno as negative */
373 # if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_AMD64_LINUX20_ENV)
374 /* Intercept the uid16 system call as used by 32bit programs. */
375 # ifdef AFS_AMD64_LINUX20_ENV
376 extern asmlinkage
long (*sys32_setgroupsp
) (int gidsetsize
, u16
* grouplist
);
377 # endif /* AFS_AMD64_LINUX20_ENV */
378 # ifdef AFS_SPARC64_LINUX26_ENV
379 extern asmlinkage
int (*sys32_setgroupsp
) (int gidsetsize
,
380 __kernel_gid32_t
* grouplist
);
381 # endif /* AFS_SPARC64_LINUX26_ENV */
383 afs32_xsetgroups(int gidsetsize
, u16
* grouplist
)
386 cred_t
*cr
= crref();
390 old_pag
= PagInCred(cr
);
393 code
= (*sys32_setgroupsp
) (gidsetsize
, grouplist
);
399 if (old_pag
!= NOPAG
&& PagInCred(cr
) == NOPAG
) {
400 /* re-install old pag if there's room. */
401 code
= __setpag(&cr
, old_pag
, &junk
, 0, NULL
);
405 /* Linux syscall ABI returns errno as negative */
409 /* Intercept the uid32 system call as used by 32bit programs. */
410 # ifdef AFS_AMD64_LINUX20_ENV
411 extern asmlinkage
long (*sys32_setgroups32p
) (int gidsetsize
, gid_t
* grouplist
);
412 # endif /* AFS_AMD64_LINUX20_ENV */
413 # ifdef AFS_SPARC64_LINUX26_ENV
414 extern asmlinkage
int (*sys32_setgroups32p
) (int gidsetsize
,
415 __kernel_gid32_t
* grouplist
);
416 # endif /* AFS_SPARC64_LINUX26_ENV */
418 afs32_xsetgroups32(int gidsetsize
, gid_t
* grouplist
)
421 cred_t
*cr
= crref();
425 old_pag
= PagInCred(cr
);
428 code
= (*sys32_setgroups32p
) (gidsetsize
, grouplist
);
434 if (old_pag
!= NOPAG
&& PagInCred(cr
) == NOPAG
) {
435 /* re-install old pag if there's room. */
436 code
= __setpag(&cr
, old_pag
, &junk
, 0, NULL
);
440 /* Linux syscall ABI returns errno as negative */
444 #endif /* !LINUX_KEYRING_SUPPORT */
446 #ifdef LINUX_KEYRING_SUPPORT
447 static void afs_pag_describe(const struct key
*key
, struct seq_file
*m
)
449 seq_puts(m
, key
->description
);
451 seq_printf(m
, ": %u", key
->datalen
);
454 #if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
455 static int afs_pag_instantiate(struct key
*key
, struct key_preparsed_payload
*prep
)
457 static int afs_pag_instantiate(struct key
*key
, const void *data
, size_t datalen
)
461 afs_uint32
*userpag
, pag
= NOPAG
;
463 if (!uid_eq(key
->uid
, GLOBAL_ROOT_UID
) || !gid_eq(key
->gid
, GLOBAL_ROOT_GID
))
467 get_group_info(current_group_info());
469 #if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
470 if (prep
->datalen
!= sizeof(afs_uint32
) || !prep
->data
)
472 if (datalen
!= sizeof(afs_uint32
) || !data
)
476 /* ensure key being set matches current pag */
477 pag
= afs_linux_pag_from_groups(current_group_info());
482 #if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
483 userpag
= (afs_uint32
*)prep
->data
;
485 userpag
= (afs_uint32
*)data
;
490 #if defined(STRUCT_KEY_HAS_PAYLOAD_VALUE)
491 key
->payload
.value
= (unsigned long) *userpag
;
493 memcpy(&key
->payload
, userpag
, sizeof(afs_uint32
));
495 key
->datalen
= sizeof(afs_uint32
);
499 put_group_info(current_group_info());
503 #if !defined(STRUCT_KEY_TYPE_HAS_MATCH_PREPARSE)
504 /* Note that we only define a ->match function if struct
505 * key_type.match_preparse does _not_ exist. If key_type.match_preparse does
506 * exist, we would use that to specify an alternative comparison function; but
507 * since we just rely on default behavior, we don't need to actually specify
508 * one. But for kernels with no such match_preparse function, we need to
509 * specify a 'match' function, since there is no default. */
510 static int afs_pag_match(const struct key
*key
, const void *description
)
512 return strcmp(key
->description
, description
) == 0;
516 static void afs_pag_destroy(struct key
*key
)
519 int locked
= ISAFS_GLOCK();
521 #if defined(STRUCT_KEY_HAS_PAYLOAD_VALUE)
522 pag
= key
->payload
.value
;
524 memcpy(&pag
, &key
->payload
, sizeof(afs_uint32
));
530 afs_MarkUserExpired(pag
);
536 struct key_type key_type_afs_pag
=
539 .describe
= afs_pag_describe
,
540 #if defined(STRUCT_KEY_TYPE_HAS_INSTANTIATE_PREP)
541 .instantiate_prep
= afs_pag_instantiate
,
544 .instantiate
= afs_pag_instantiate
,
546 #if !defined(STRUCT_KEY_TYPE_HAS_MATCH_PREPARSE)
547 .match
= afs_pag_match
,
549 .destroy
= afs_pag_destroy
,
552 #ifdef EXPORTED_TASKLIST_LOCK
553 extern rwlock_t tasklist_lock
__attribute__((weak
));
556 void osi_keyring_init(void)
558 #if !defined(EXPORTED_KEY_TYPE_KEYRING)
559 struct task_struct
*p
;
561 /* If we can't lock the tasklist, either with its explicit lock,
562 * or by using the RCU lock, then we can't safely work out the
563 * type of a keyring. So, we have to rely on the weak reference.
564 * If that's not available, then keyring based PAGs won't work.
567 #if defined(EXPORTED_TASKLIST_LOCK) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) && defined(HAVE_LINUX_RCU_READ_LOCK))
568 if (__key_type_keyring
== NULL
) {
569 # ifdef EXPORTED_TASKLIST_LOCK
571 read_lock(&tasklist_lock
);
573 # if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) && defined(HAVE_LINUX_RCU_READ_LOCK))
574 # ifdef EXPORTED_TASKLIST_LOCK
579 #if defined(HAVE_LINUX_FIND_TASK_BY_PID)
580 p
= find_task_by_pid(1);
582 p
= find_task_by_vpid(1);
584 if (p
&& task_user(p
)->session_keyring
)
585 __key_type_keyring
= task_user(p
)->session_keyring
->type
;
586 # ifdef EXPORTED_TASKLIST_LOCK
588 read_unlock(&tasklist_lock
);
590 # if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) && defined(HAVE_LINUX_RCU_READ_LOCK))
591 # ifdef EXPORTED_TASKLIST_LOCK
600 register_key_type(&key_type_afs_pag
);
603 void osi_keyring_shutdown(void)
605 unregister_key_type(&key_type_afs_pag
);
609 osi_get_keyring_pag(afs_ucred_t
*cred
)
613 afs_int32 keyring_pag
= NOPAG
;
615 if (afs_cr_rgid(cred
) != NFSXLATOR_CRED
) {
616 key
= afs_linux_search_keyring(cred
, &key_type_afs_pag
);
619 if (key_validate(key
) == 0 && uid_eq(key
->uid
, GLOBAL_ROOT_UID
)) { /* also verify in the session keyring? */
620 #if defined(STRUCT_KEY_HAS_PAYLOAD_VALUE)
621 keyring_pag
= key
->payload
.value
;
623 memcpy(&keyring_pag
, &key
->payload
, sizeof(afs_int32
));
625 /* Only set PAG in groups if needed,
626 * and the creds are from the current process */
627 if (afs_linux_cred_is_current(cred
) &&
628 ((keyring_pag
>> 24) & 0xff) == 'A' &&
629 keyring_pag
!= afs_linux_pag_from_groups(current_group_info())) {
630 __setpag(&cred
, keyring_pag
, &newpag
, 0, NULL
);
640 void osi_keyring_init(void)
645 void osi_keyring_shutdown(void)