[OpenAFS-devel] keyring/pag support for linux

chas williams - CONTRACTOR chas@cmf.nrl.navy.mil
Wed, 02 Aug 2006 09:31:35 -0400


"chas williams - CONTRACTOR" writes:
>attached is another attempt at pag via keyring support.  again, this

and yet another version.  this time, you dont (or shouldnt) need changes
to your local kernel tree.  we have our own version of join_session_keyring()
which uses the public bits of the keyring api and a bit of information from
task 1 to allocate and link a new session keyring.

what we really need from the linux kernel: the keyring key_type exported,
the keyring primitives exported, or join_session_keyring() exported.
the first time might suffer a bit when it comes to safely inserting
the session_key into the user task since the rcu primitives are not
available to openafs, so the third option is preferred.

you might need to define KEY_ALLOC_NEEDS_STRUCT_TASK if you have a
rather newish kernel (>= 2.6.17).  i have a configure test but its
against other configure changes so its useless.

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;
@@ -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 CONFIG_KEYS
+    if (pag == NOPAG) {
+	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,11 @@
  */
 #include <afsconfig.h>
 #include "afs/param.h"
+#ifdef CONFIG_KEYS
+#include <linux/rwsem.h>
+#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 +153,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 +190,107 @@
     put_group_info(group_info);
 
     return 0;
+}
+
+struct key_type *__key_type_keyring;
+
+#ifndef KEY_ALLOC_IN_QUOTA
+#define KEY_ALLOC_IN_QUOTA 1
+#endif
+
+static long
+__join_session_keyring(void *name)
+{
+    struct key *keyring, *old;
+    char desc[20];
+    unsigned long not_in_quota;
+    int code = 0;
+
+    if (!name) {
+	/* create an empty session keyring */
+	not_in_quota = KEY_ALLOC_IN_QUOTA;
+	sprintf(desc, "_ses.%u", current->tgid);
+
+#ifdef KEY_ALLOC_NEEDS_STRUCT_TASK
+        keyring = key_alloc(__key_type_keyring, desc,
+                            current->uid, current->gid, current,
+                            (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
+                            not_in_quota);
 #else
+        keyring = key_alloc(__key_type_keyring, desc,
+                            current->uid, current->gid,
+                            (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
+                            not_in_quota);
+#endif
+
+        if (IS_ERR(keyring)) {
+	    code = PTR_ERR(keyring);
+	    goto out;
+	}
+
+	code = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
+	if (code < 0) {
+	    key_put(keyring);
+	    goto out;
+	}
+
+        /* install the keyring */
+        spin_lock_irq(&current->sighand->siglock);
+        old = current->signal->session_keyring;
+        current->signal->session_keyring = keyring;
+        spin_unlock_irq(&current->sighand->siglock);
+
+        if (old)
+	    key_put(old);
+    }
+
+out:
+    return code;
+}
+
+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); */
+	__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(&afspag, "_pag", 0, 0, current, perm, 1);
+#else
+	    key = key_alloc(&afspag, "_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 /* 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 +324,8 @@
 
     osi_Free((char *)gidset, NGROUPS * sizeof(int));
     return code;
+}
 #endif
-}
 
 
 /* Intercept the standard system call. */
@@ -396,3 +501,93 @@
 #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)
+{
+    struct task_struct *p;
+
+    p = find_task_by_pid(1);
+    if (p && p->user->session_keyring)
+	__key_type_keyring = p->user->session_keyring->type;
+
+    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 */
@@ -675,7 +680,6 @@
 /* afs_server.c */
 extern afs_rwlock_t afs_xsrvAddr;
 extern afs_rwlock_t afs_xserver;
-extern afs_rwlock_t afs_icl_lock;
 extern struct srvAddr *afs_srvAddrs[NSERVERS];
 extern struct server *afs_servers[NSERVERS];
 extern int afs_totalServers;