[OpenAFS-devel] keyring/pag support for linux

chas williams - CONTRACTOR chas@cmf.nrl.navy.mil
Fri, 04 Aug 2006 07:44:06 -0400


In message <E02A18B3D77D8E4D22FF4249@bistromath.pc.cs.cmu.edu>,Jeffrey Hutzelma
n writes:
>Hm; actually, I see David says that will work in the kernel.  I guess I can 
>believe that.

it does.  here is yet another version of the keyring code that uses
this facility.  depressingly the ia64 lacks syscall2() and i am not
certain this will work on ia64 given that the kernel uses a fixed gp
and a module is gp relative (i guess it should since userspace is
gp relative as well).  this would make things happy for the bulk of
the users. ia64 can hopefully be fixed later when i actually get
a sles10 kernel built with CONFIG_KEYS.

some function names were changed a bit in hopes of preventing confusion
with the pag manager in the linux afs/nfs translator.  the configure
tests rely need AC_TRY_KBUILD.

Index: src/afs/afs_osi_pag.c
===================================================================
--- src/afs/afs_osi_pag.c	(revision 38)
+++ 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;
@@ -487,7 +485,7 @@
     gid_t g0, g1;
 
     AFS_STATCNT(PagInCred);
-    if (cred == NULL) {
+    if (cred == NULL || cred == afs_osi_credp) {
 	return NOPAG;
     }
 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
@@ -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 LINUX_KEYRING_SUPPORT
+    if (pag == NOPAG) {
+	struct key *key;
+	afs_uint32 pag, newpag;
+
+	key = request_key(&key_type_afs_pag, "_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 38)
+++ src/afs/LINUX/osi_groups.c	(working copy)
@@ -15,6 +15,9 @@
  */
 #include <afsconfig.h>
 #include "afs/param.h"
+#ifdef LINUX_KEYRING_SUPPORT
+#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 +151,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 +188,64 @@
     put_group_info(group_info);
 
     return 0;
+}
+
+#ifdef LINUX_KEYRING_SUPPORT
+#include <asm/unistd.h>
+#include <linux/keyctl.h>
+
+static int errno;
+static inline _syscall2(long, keyctl, int, option, void*, arg2);
+
+static long
+__join_session_keyring(char *name)
+{
+	return keyctl(KEYCTL_JOIN_SESSION_KEYRING, name);
+}
+#endif /* LINUX_KEYRING_SUPPORT */
+
+int
+setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
+         int change_parent)
+{
+    int code;
+
+    code = __setpag(cr, pagvalue, newpag, change_parent);
+
+#ifdef LINUX_KEYRING_SUPPORT
+    if (code == 0) {
+
+	(void) __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;
+
+#ifdef KEY_ALLOC_NEEDS_STRUCT_TASK
+	    key = key_alloc(&key_type_afs_pag, "_pag", 0, 0, current, perm, 1);
 #else
+	    key = key_alloc(&key_type_afs_pag, "_pag", 0, 0, perm, 1);
+#endif
+
+	    if (!IS_ERR(key)) {
+		key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
+					 current->signal->session_keyring, NULL);
+		key_put(key);
+	    }
+	}
+    }
+#endif /* LINUX_KEYRING_SUPPORT */
+
+    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 +279,8 @@
 
     osi_Free((char *)gidset, NGROUPS * sizeof(int));
     return code;
+}
 #endif
-}
 
 
 /* Intercept the standard system call. */
@@ -396,3 +456,86 @@
 #endif
 #endif
 
+
+#ifdef LINUX_KEYRING_SUPPORT
+static void afs_pag_describe(const struct key *key, struct seq_file *m)
+{
+    seq_puts(m, key->description);
+
+    seq_printf(m, ": %u", key->datalen);
+}
+
+static int afs_pag_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 afs_pag_match(const struct key *key, const void *description)
+{
+	return strcmp(key->description, description) == 0;
+}
+
+struct key_type key_type_afs_pag =
+{
+    .name        = "afs_pag",
+    .describe    = afs_pag_describe,
+    .instantiate = afs_pag_instantiate,
+    .match       = afs_pag_match,
+};
+
+void osi_keyring_init(void)
+{
+    register_key_type(&key_type_afs_pag);
+}
+
+void osi_keyring_shutdown(void)
+{
+    unregister_key_type(&key_type_afs_pag);
+}
+
+#else
+void osi_keyring_init(void)
+{
+	return;
+}
+
+void osi_keyring_shutdown(void)
+{
+	return;
+}
+#endif
Index: src/afs/LINUX/osi_module.c
===================================================================
--- src/afs/LINUX/osi_module.c	(revision 38)
+++ src/afs/LINUX/osi_module.c	(working copy)
@@ -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_keyring_init();
 #ifdef AFS_LINUX24_ENV
     afsproc_init();
 #endif
@@ -389,6 +385,7 @@
 cleanup_module(void)
 #endif
 {
+    osi_keyring_shutdown();
     osi_sysctl_clean();
     osi_syscall_clean();
     unregister_filesystem(&afs_fs_type);
Index: src/afs/LINUX/osi_prototypes.h
===================================================================
--- src/afs/LINUX/osi_prototypes.h	(revision 38)
+++ src/afs/LINUX/osi_prototypes.h	(working copy)
@@ -67,4 +67,14 @@
 /* osi_vnodeops.c */
 extern void afs_fill_inode(struct inode *ip, struct vattr *vattr);
 
+/* osi_groups.c */
+extern void osi_keyring_init(void);
+extern void osi_keyring_shutdown(void);
+extern int __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
+		     int change_parent);
+#ifdef LINUX_KEYRING_SUPPORT
+extern struct key_type key_type_afs_pag;
+#endif /* LINUX_KEYRING_SUPPORT */
+
+
 #endif /* _OSI_PROTO_H_ */
Index: src/afs/sysincludes.h
===================================================================
--- src/afs/sysincludes.h	(revision 38)
+++ src/afs/sysincludes.h	(working copy)
@@ -70,7 +70,11 @@
 #include <linux/security.h>
 #endif
 #include <linux/suspend.h>
+#if defined(LINUX_KEYRING_SUPPORT)
+#include <linux/rwsem.h>
+#include <linux/key.h>
 #endif
+#endif
 /* Avoid conflicts with coda overloading AFS type namespace. Must precede
  * inclusion of uaccess.h.
  */
Index: src/cf/linux-test4.m4
===================================================================
--- src/cf/linux-test4.m4	(revision 38)
+++ src/cf/linux-test4.m4	(working copy)
@@ -587,3 +587,41 @@
       ac_cv_linux_get_sb_has_struct_vfsmount=yes,
       ac_cv_linux_get_sb_has_struct_vfsmount=no)])
   AC_MSG_RESULT($ac_cv_linux_get_sb_has_struct_vfsmount)])
+
+AC_DEFUN([LINUX_LINUX_KEYRING_SUPPORT], [
+  AC_MSG_CHECKING([for linux kernel keyring support])
+  AC_CACHE_VAL([ac_cv_linux_keyring_support], [
+    AC_TRY_KBUILD(
+[#include <linux/rwsem.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <asm/unistd.h>
+static int errno;
+static inline _syscall2(long, keyctl, int, option, void*, arg2);],
+[#ifdef CONFIG_KEYS
+keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL);
+request_key(NULL, NULL, NULL);
+#else
+#error rebuild your kernel with CONFIG_KEYS
+#endif],
+      ac_cv_linux_keyring_support=yes,
+      ac_cv_linux_keyring_support=no)])
+  AC_MSG_RESULT($ac_cv_linux_keyring_support)
+  if test "x$ac_cv_linux_keyring_support" = "xyes"; then
+    AC_DEFINE([LINUX_KEYRING_SUPPORT], 1, [define if your kernel has keyring support])
+  fi])
+
+AC_DEFUN([LINUX_KEY_ALLOC_NEEDS_STRUCT_TASK], [
+  AC_MSG_CHECKING([if key_alloc() takes a struct task *])
+  AC_CACHE_VAL([ac_cv_key_alloc_needs_struct_task], [
+    AC_TRY_KBUILD(
+[#include <linux/rwsem.h>
+#include <linux/key.h>
+],
+[(void) key_alloc(NULL, NULL, 0, 0, NULL, 0, 0);],
+      ac_cv_key_alloc_needs_struct_task=yes,
+      ac_cv_key_alloc_needs_struct_task=no)])
+  AC_MSG_RESULT($ac_cv_key_alloc_needs_struct_task)
+  if test "x$ac_cv_key_alloc_needs_struct_task" = "xyes"; then
+    AC_DEFINE([KEY_ALLOC_NEEDS_STRUCT_TASK], 1, [define if key_alloc takes a struct task *])
+  fi])
Index: acinclude.m4
===================================================================
--- acinclude.m4	(revision 38)
+++ acinclude.m4	(working copy)
@@ -603,6 +603,8 @@
 		 LINUX_SCHED_STRUCT_TASK_STRUCT_HAS_SIGNAL_RLIM
 		 LINUX_SCHED_STRUCT_TASK_STRUCT_HAS_EXIT_STATE
 		 LINUX_REFRIGERATOR
+		 LINUX_LINUX_KEYRING_SUPPORT
+		 LINUX_KEY_ALLOC_NEEDS_STRUCT_TASK
 		 LINUX_WHICH_MODULES
                  if test "x$ac_cv_linux_config_modversions" = "xno" -o $AFS_SYSKVERS -ge 26; then
                    AC_MSG_WARN([Cannot determine sys_call_table status. assuming it isn't exported])