[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])