[OpenAFS-devel] keyring/pag support for linux

chas williams chas@cmf.nrl.navy.mil
Sun, 16 Jul 2006 12:23:23 -0400


below is a sample implementation of keyring/pag support for linux kernels.
i had access to some previous code (thanks derrick) but some of the
kernel interfaces used in keyring have changed since that code compiled.
this led me down a slightly different path.

it seems like a bad idea to duplicate some of the work the kernel
already does (like installing a new session keyring), but we cant get
access to these routines from a kernel module.  so, the only part that
is done in the kernel is searching the keyring for an appropriate key.
this key holds the pag and gets inserted from userspace during setpag().
PagInCred only looks for this key when the current process doesnt have
a pag, and reinserts the necessary groups.  this isnt strictly necessary
but it changes the current behavior the least.

pagsh uses the keyctl() to create a new anonymous session keyring before
spawning the shell.  its not clear if this should be done in setpag().
it would atleast need to be done in whatever login you use (login.krb5
in my case).

the drawback to this, is that you need the keyutils library (and any
program using setpag() will need to link with the keyutils as well).

in my test case, i start pagsh, get some credentials and create a file.
then i su to myself and create another file.  typically this would drop
the pag but you can see the keyring followed me, and i still have my pag.
and of course, when the pagsh ends, my pag disappears as well.

	...chas/tmp/openafs/trunk/2.6.17 relax.8% touch /afs/cmf/users/chas/testfile
	touch: cannot touch `/afs/cmf/users/chas/testfile': Permission denied

	...chas/tmp/openafs/trunk/2.6.17 relax.9% src/sys/pagsh
	calling lsetpag()
	%id -a
	uid=1025(chas) gid=0(root) groups=34280,48266,0(root)
	%keyctl show
	Session Keyring
	       -3 --alswrv   1025     0  keyring: _ses.5324
	272782323 --als-rv   1025     0   \_ afspag: _pag.1102724490
	%aklog
	%tokens

	Tokens held by the Cache Manager:

	User's (AFS ID 1025) tokens for afs@cmf.nrl.navy.mil [Expires Jul 16 16:48]
	   --End of list--
	%touch /afs/cmf/users/chas/testfile
	%su - chas
	Password: 

	~ relax.1% keyctl show
	Session Keyring
	       -3 --alswrv   1025     0  keyring: _ses.5324
	272782323 --als-rv   1025     0   \_ afspag: _pag.1102724490
	~ relax.2% touch /afs/cmf/users/chas/testfile2
	~ relax.3% logout

	%exit
	...chas/tmp/openafs/trunk/2.6.17 relax.10% touch /afs/cmf/users/chas/testfile3
	touch: cannot touch `/afs/cmf/users/chas/testfile3': Permission denied

yes, the key has the pag in its name.  i know this is stupid, but it is
handy for debugging.  it probably should be just named _pag since i cant
see why any particular session would have more than one pag?

comments?

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,21 @@
 #endif
 #endif
     pag = (afs_int32) afs_get_pag_from_groups(g0, g1);
+out:
+#ifdef CONFIG_KEYS
+    if (pag == NOPAG) {
+	extern struct key_type afspag;
+	struct key *key;
+	afs_uint32 pag;
+
+	key = request_key(&afspag, "_pag.", NULL);
+	if (!IS_ERR(key)) {
+	    pag = (afs_uint32) key->payload.value;
+	    if (((pag >> 24) & 0xff) == 'A')
+		AddPag(pag, &cred);
+	    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 $");
@@ -396,3 +400,89 @@
 #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 = -EINVAL;
+    afs_uint32 *pag;
+
+    if (datalen <= 0 || datalen > 32767 || !data)
+	goto error;
+
+    code = key_payload_reserve(key, datalen);
+    if (code == 0) {
+	pag = (afs_uint32 *) data;
+
+	key->payload.value = (unsigned long) *pag;
+	key->datalen = sizeof(afs_uint32);
+    }
+
+error:
+    return code;
+}
+
+
+static int afspag_match(const struct key *key, const void *description)
+{
+	/* partial match */
+	return strncmp(key->description, description, 5) == 0;
+}
+
+static long afspag_read(const struct key *key,
+			 char __user *buffer, size_t len)
+{
+    long code = key->datalen;
+
+    /* we can return the data as is */
+    if (buffer && len > 0) {
+	if (len > key->datalen)
+	    len = key->datalen;
+
+	if (copy_to_user(buffer, key->payload.data, len) != 0)
+	    code = -EFAULT;
+    }
+
+    return code;
+}
+
+struct key_type afspag =
+{
+    .name        = "afspag",
+    .def_datalen = sizeof(afs_uint32),
+    .describe    = afspag_describe,
+    .instantiate = afspag_instantiate,
+    .match       = afspag_match,
+    .read        = afspag_read,
+/*  .update      = afspag_instantiate */
+};
+
+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/sys/rmtsysc.c
===================================================================
--- src/sys/rmtsysc.c	(revision 3)
+++ src/sys/rmtsysc.c	(working copy)
@@ -163,6 +163,7 @@
     if (!(conn = rx_connection(&errorcode, "setpag"))) {
 	/* Remote call can't be performed for some reason.
 	 * Try the local 'setpag' system call ... */
+fprintf(stderr, "calling lsetpag()\n");
 	errorcode = lsetpag();
 	return errorcode;
     }
@@ -301,8 +302,6 @@
 	/* Additional testing */
 	if (((result >> 24) & 0xff) == 'A')
 	    return result;
-	else
-	    return NOPAG;
     }
     return NOPAG;
 }
Index: src/sys/setpag.c
===================================================================
--- src/sys/setpag.c	(revision 3)
+++ src/sys/setpag.c	(working copy)
@@ -24,6 +24,9 @@
 #else
 #include <stdio.h>
 #endif
+#ifdef HAVE_ADD_KEY
+#include <keyutils.h>
+#endif
 #include "afssyscalls.h"
 
 #ifdef AFS_AIX32_ENV
@@ -55,6 +58,29 @@
     
     if(rval)
       errcode = syscall(AFS_SYSCALL, AFSCALL_SETPAG);
+
+#ifdef HAVE_ADD_KEY
+#define NOPAG 0xffffffff
+
+{
+    gid_t groups[16];
+    int ngroups = 16;
+
+    ngroups = getgroups(ngroups, groups);
+    if (ngroups >= 2) {
+	afs_uint32 pag;
+
+	pag = afs_get_pag_from_groups(groups[0], groups[1]);
+	if (pag != NOPAG) {
+	    char desc[16];
+
+	    sprintf(desc, "_pag.%u", pag);
+	    (void) add_key("afspag", desc, (void *) &pag, sizeof(afs_uint32), KEY_SPEC_SESSION_KEYRING);
+	}
+    }
+}
+#endif /* HAVE_ADD_KEY */
+
 #else
     errcode = syscall(AFS_SYSCALL, AFSCALL_SETPAG);
 #endif
Index: src/sys/pagsh.c
===================================================================
--- src/sys/pagsh.c	(revision 3)
+++ src/sys/pagsh.c	(working copy)
@@ -30,6 +30,9 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #endif
+#ifdef HAVE_ADD_KEY
+#include <keyutils.h>
+#endif
 
 #include "AFS_component_version_number.c"
 
@@ -64,6 +67,9 @@
     } else {
 /*		shell = pwe->pw_shell; */
     }
+#ifdef HAVE_ADD_KEY
+    (void) keyctl_join_session_keyring(NULL);
+#endif
     if (setpag() == -1) {
 	perror("setpag");
     }
Index: src/cf/osconf.m4
===================================================================
--- src/cf/osconf.m4	(revision 3)
+++ src/cf/osconf.m4	(working copy)
@@ -7,7 +7,7 @@
 CC="cc"
 CCOBJ="cc"
 MT_CC="cc"
-XLIBS="${LIB_AFSDB}"
+XLIBS="$XLIBS ${LIB_AFSDB}"
 
 dnl debugging and optimization flag defaults
 dnl Note, these are all the defaults for if debug/optimize turned on, and
Index: acinclude.m4
===================================================================
--- acinclude.m4	(revision 33)
+++ acinclude.m4	(working copy)
@@ -104,7 +104,8 @@
 system=$host
 case $system in
         *-linux*)
-
+		AC_CHECK_LIB(keyutils, add_key, XLIBS="$XLIBS -lkeyutils"
+					        AC_DEFINE(HAVE_ADD_KEY, 1, [define if you have add_key in keyutils]))
 		MKAFS_OSTYPE=LINUX
 		if test "x$enable_redhat_buildsys" = "xyes"; then
 		 AC_DEFINE(ENABLE_REDHAT_BUILDSYS, 1, [define if you have redhat buildsystem])