[OpenAFS-devel] keyring/pag support for linux

chas williams - CONTRACTOR chas@cmf.nrl.navy.mil
Sat, 22 Jul 2006 09:09:04 -0400


In message <Pine.GSO.4.61-042.0607201012440.4811@johnstown.andrew.cmu.edu>,Derr
ick J Brashear writes:
>Me either. I also prefer it when software engineers don't pretend to be 
>lawyers.

attached is another attempt at pag via keyring support.  again, this
version uses the keyring to cache the pag value and will reinsert the pag
groups as necessary.  this version attempts to make the keys immutable
by non-root users, ensures that you are setting the actual pag value
and prevents users from creating keys.

this version does NOT modify any of the userspace and is completely
inside the kernel (which is how we can keep users from creating arbitrary
keys and trying to find pag groups).  so it doesnt require the keyutils
library either.  however, i needed to make one small change to the kernel:
join_session_keyring() needs to be exported since its difficult to write
this code correctly without access to the rcu routines.  three solutions:
create and install a session keyring in a not-so-safe manner that is
probably "good enough", get the linux kernel to export the function, or
write a small bridge module that provides this function.  (the module
would have to be the LGPL so that it could get access to the GPL rcu
and provide a "library" interface that can be transparently upgraded
but still used by openafs.)  it would still be annoying to have to track
the functionality of join_session_keyring() though.

one more thing, shouldn't afs_osi_credp be called anon_credp?

a sample session:

	~ relax.1% id -a
	uid=1025(chas) gid=0(root) groups=34312,39426,0(root)
	~ relax.2% keyctl show
	Session Keyring
	       -3 --alswrv      0     0  keyring: _ses.31747
	483418457 ----s--v      0     0   \_ afspag: _pag
	~ relax.3% su - chas
	Password: 

	~ relax.1% keyctl show
	Session Keyring
	       -3 --alswrv      0     0  keyring: _ses.31747
	483418457 ----s--v      0     0   \_ afspag: _pag
	~ relax.2% id -a
	uid=1025(chas) gid=0(root) groups=34312,39426,0(root)
	~ relax.3% tokens

	Tokens held by the Cache Manager:

	User's (AFS ID 1025) tokens for afs@cmf.nrl.navy.mil [Expires Jul 22 16:59]
	   --End of list--
	~ relax.4% logout

	~ relax.4% keyctl add afspag newpag abcd @s
	add_key: Operation not permitted
	~ relax.5% su root
	Password: 
	[chas@relax chas]# keyctl add afspag newpag abcd @s
	add_key: Invalid argument
	[chas@relax chas]# id -a
	uid=0(root) gid=0(root) groups=34312,39426,0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
	[chas@relax chas]# keyctl show
	Session Keyring
	       -3 --alswrv      0     0  keyring: _ses.31747
	483418457 ----s--v      0     0   \_ afspag: _pag
	[chas@relax chas]# suspend
	~ relax.7% /usr/afsws/bin/pagsh
	%keyctl show
	Session Keyring
	       -3 --alswrv   1025     0  keyring: _ses.31851
	850171527 ----s--v      0     0   \_ afspag: _pag
	%id -a
	uid=1025(chas) gid=0(root) groups=34312,39427,0(root)
	%^D

	~ relax.17% keyctl describe 483418457
	483418457: --s--v--s--v------------     0     0 afspag: _pag
	~ relax.18% keyctl print 483418457
	keyctl_read_alloc: Operation not supported
	~ relax.19% keyctl read 483418457
	keyctl_read_alloc: Operation not supported
	~ relax.20% keyctl revoke 483418457
	keyctl_revoke: Permission denied
	~ relax.21% keyctl clear 483418457
	keyctl_clear: Permission denied
	~ relax.22% keyctl setperm 483418457 777
	keyctl_setperm: Permission denied
	~ relax.23% keyctl newring blah @s
	725749696
	~ relax.24% keyctl show
	Session Keyring
	       -3 --alswrv      0     0  keyring: _ses.31747
	483418457 ----s--v      0     0   \_ afspag: _pag
	725749696 --alswrv   1025     0   \_ keyring: blah
	~ relax.25% keyctl link 483418457 725749696
	keyctl_link: Permission denied


>Also, I'd like a pony.

                .''
      ._.-.___.' (`\
     //(        ( `'
    '/ )\ ).__. )
    ' <' `\ ._/'\
       `   \     \


Index: src/afs/afs_osi_pag.c
===================================================================
--- src/afs/afs_osi_pag.c	(revision 3)
+++ src/afs/afs_osi_pag.c	(working copy)
@@ -453,8 +453,6 @@
 	/* Additional testing */
 	if (((ret >> 24) & 0xff) == 'A')
 	    return ret;
-	else
-	    return NOPAG;
 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
     }
     return NOPAG;
@@ -509,11 +507,15 @@
 	return NOPAG;
     }
 #elif defined(AFS_LINUX26_ENV)
-    if (cred->cr_group_info->ngroups < 2)
-	return NOPAG;
+    if (cred->cr_group_info->ngroups < 2) {
+	pag = NOPAG;
+	goto out;
+    }
 #elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DUX40_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_XBSD_ENV)
-    if (cred->cr_ngroups < 2)
-	return NOPAG;
+    if (cred->cr_ngroups < 2) {
+	pag = NOPAG;
+	goto out;
+    }
 #endif
 #if defined(AFS_AIX51_ENV)
     g0 = cred->cr_groupset.gs_union.un_groups[0];
@@ -527,5 +529,23 @@
 #endif
 #endif
     pag = (afs_int32) afs_get_pag_from_groups(g0, g1);
+out:
+#ifdef CONFIG_KEYS
+    if (pag == NOPAG && cred != afs_osi_credp) {
+	struct key *key;
+	afs_uint32 pag, newpag;
+
+	key = request_key(&afspag, "_pag", NULL);
+	if (!IS_ERR(key)) {
+	    if (key_validate(key) == 0 && key->uid == 0) {	/* also verify in the session keyring? */
+
+		pag = (afs_uint32) key->payload.value;
+		if (((pag >> 24) & 0xff) == 'A')
+		    __setpag(&cred, pag, &newpag, 0);
+	    }
+	    key_put(key);
+	} 
+    }
+#endif
     return pag;
 }
Index: src/afs/LINUX/osi_groups.c
===================================================================
--- src/afs/LINUX/osi_groups.c	(revision 3)
+++ src/afs/LINUX/osi_groups.c	(working copy)
@@ -15,6 +15,10 @@
  */
 #include <afsconfig.h>
 #include "afs/param.h"
+#ifdef CONFIG_KEYS
+#include <linux/key.h>
+#include <linux/seq_file.h>
+#endif
 
 RCSID
     ("$Header: /cvs/openafs/src/afs/LINUX/osi_groups.c,v 1.25.2.3 2005/08/02 05:16:33 shadow Exp $");
@@ -148,11 +152,11 @@
 }
 #endif
 
+#if defined(AFS_LINUX26_ENV)
 int
-setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
-       int change_parent)
+__setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
+         int change_parent)
 {
-#if defined(AFS_LINUX26_ENV)
     struct group_info *group_info;
     gid_t g0, g1;
     struct group_info *tmp;
@@ -185,7 +189,46 @@
     put_group_info(group_info);
 
     return 0;
+}
+
+int
+setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
+         int change_parent)
+{
+    int code;
+
+    code = __setpag(cr, pagvalue, newpag, change_parent);
+
+#ifdef CONFIG_KEYS
+    if (code == 0) {
+
+	join_session_keyring(NULL);
+
+	if (current->signal->session_keyring) {
+	    struct key *key;
+	    key_perm_t perm;
+
+	    perm = KEY_POS_VIEW | KEY_POS_SEARCH;
+	    perm |= KEY_USR_VIEW | KEY_USR_SEARCH;
+
+	    key = key_alloc(&afspag, "_pag", 0, 0, current, perm, 1);
+
+	    if (!IS_ERR(key)) {
+		key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
+					 current->signal->session_keyring, NULL);
+		key_put(key);
+	    }
+	}
+    }
+#endif /* CONFIG_KEYS */
+
+    return code;
+}
 #else
+int
+setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
+       int change_parent)
+{
     gid_t *gidset;
     afs_int32 ngroups, code = 0;
     int j;
@@ -219,8 +262,8 @@
 
     osi_Free((char *)gidset, NGROUPS * sizeof(int));
     return code;
+}
 #endif
-}
 
 
 /* Intercept the standard system call. */
@@ -396,3 +439,87 @@
 #endif
 #endif
 
+
+#ifdef CONFIG_KEYS
+static void afspag_describe(const struct key *key, struct seq_file *m)
+{
+    seq_puts(m, key->description);
+
+    seq_printf(m, ": %u", key->datalen);
+}
+
+static int afspag_instantiate(struct key *key, const void *data, size_t datalen)
+{
+    int code;
+    afs_uint32 *userpag, pag = NOPAG;
+    int g0, g1;
+
+    if (key->uid != 0 || key->gid != 0)
+	return -EPERM;
+
+    code = -EINVAL;
+    get_group_info(current->group_info);
+
+    if (datalen != sizeof(afs_uint32) || !data)
+	goto error;
+
+    if (current->group_info->ngroups < 2)
+	goto error;
+
+    /* ensure key being set matches current pag */
+
+    g0 = GROUP_AT(current->group_info, 0);
+    g1 = GROUP_AT(current->group_info, 1);
+
+    pag = afs_get_pag_from_groups(g0, g1);
+    if (pag == NOPAG)
+	goto error;
+
+    userpag = (afs_uint32 *) data;
+    if (*userpag != pag)
+	goto error;
+
+    key->payload.value = (unsigned long) *userpag;
+    key->datalen = sizeof(afs_uint32);
+    code = 0;
+
+error:
+    put_group_info(current->group_info);
+    return code;
+}
+
+static int afspag_match(const struct key *key, const void *description)
+{
+	return strcmp(key->description, description) == 0;
+}
+
+struct key_type afspag =
+{
+    .name        = "afspag",
+    .describe    = afspag_describe,
+    .instantiate = afspag_instantiate,
+    .match       = afspag_match,
+};
+
+void osi_pag_init(void)
+{
+    register_key_type(&afspag);
+}
+
+void osi_pag_shutdown(void)
+{
+    /* what to do about existing keys? */
+    unregister_key_type(&afspag);
+}
+
+#else
+void osi_pag_init(void)
+{
+	return;
+}
+
+void osi_pag_shutdown(void)
+{
+	return;
+}
+#endif
Index: src/afs/LINUX/osi_module.c
===================================================================
--- src/afs/LINUX/osi_module.c	(revision 3)
+++ src/afs/LINUX/osi_module.c	(working copy)
@@ -48,13 +48,6 @@
 static long get_page_offset(void);
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
-DEFINE_MUTEX(afs_global_lock);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-DECLARE_MUTEX(afs_global_lock);
-#else
-struct semaphore afs_global_lock = MUTEX;
-#endif
 int afs_global_owner = 0;
 #if !defined(AFS_LINUX24_ENV)
 unsigned long afs_linux_page_offset = 0;	/* contains the PAGE_OFFSET value */
@@ -366,14 +359,17 @@
 
     osi_Init();
 
+#ifdef notdef
     err = osi_syscall_init();
     if (err)
 	return err;
+#endif
     err = afs_init_inodecache();
     if (err)
 	return err;
     register_filesystem(&afs_fs_type);
     osi_sysctl_init();
+    osi_pag_init();
 #ifdef AFS_LINUX24_ENV
     afsproc_init();
 #endif
@@ -389,6 +385,7 @@
 cleanup_module(void)
 #endif
 {
+    osi_pag_shutdown();
     osi_sysctl_clean();
     osi_syscall_clean();
     unregister_filesystem(&afs_fs_type);
Index: src/afs/afs_prototypes.h
===================================================================
--- src/afs/afs_prototypes.h	(revision 3)
+++ src/afs/afs_prototypes.h	(working copy)
@@ -551,7 +551,12 @@
 extern void osi_linux_rxkreg(void);
 extern int setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
 		  int change_parent);
+extern int __setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
+		  int change_parent);
+#ifdef CONFIG_KEYS
+extern struct key_type afspag;
 #endif
+#endif
 
 
 /* ARCH/osi_sleep.c */