fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / sunrpc / auth.c
index 694301a..f4b8344 100644 (file)
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
-#include <linux/socket.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/spinlock.h>
-#include <linux/vserver/xid.h>
+#include <linux/vs_tag.h>
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY       RPCDBG_AUTH
@@ -26,7 +25,7 @@ static struct rpc_authops *   auth_flavors[RPC_AUTH_MAXFLAVOR] = {
        NULL,                   /* others can be loadable modules */
 };
 
-u32
+static u32
 pseudoflavor_to_flavor(u32 flavor) {
        if (flavor >= RPC_AUTH_MAXFLAVOR)
                return RPC_AUTH_GSS;
@@ -66,17 +65,26 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
        struct rpc_authops      *ops;
        u32                     flavor = pseudoflavor_to_flavor(pseudoflavor);
 
-       if (flavor >= RPC_AUTH_MAXFLAVOR || !(ops = auth_flavors[flavor]))
-               return NULL;
-       if (!try_module_get(ops->owner))
-               return NULL;
+       auth = ERR_PTR(-EINVAL);
+       if (flavor >= RPC_AUTH_MAXFLAVOR)
+               goto out;
+
+       /* FIXME - auth_flavors[] really needs an rw lock,
+        * and module refcounting. */
+#ifdef CONFIG_KMOD
+       if ((ops = auth_flavors[flavor]) == NULL)
+               request_module("rpc-auth-%u", flavor);
+#endif
+       if ((ops = auth_flavors[flavor]) == NULL)
+               goto out;
        auth = ops->create(clnt, pseudoflavor);
-       if (!auth)
-               return NULL;
-       atomic_set(&auth->au_count, 1);
+       if (IS_ERR(auth))
+               return auth;
        if (clnt->cl_auth)
                rpcauth_destroy(clnt->cl_auth);
        clnt->cl_auth = auth;
+
+out:
        return auth;
 }
 
@@ -86,51 +94,42 @@ rpcauth_destroy(struct rpc_auth *auth)
        if (!atomic_dec_and_test(&auth->au_count))
                return;
        auth->au_ops->destroy(auth);
-       module_put(auth->au_ops->owner);
-       kfree(auth);
 }
 
-static spinlock_t rpc_credcache_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(rpc_credcache_lock);
 
 /*
  * Initialize RPC credential cache
  */
-void
-rpcauth_init_credcache(struct rpc_auth *auth)
+int
+rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
 {
+       struct rpc_cred_cache *new;
        int i;
-       for (i = 0; i < RPC_CREDCACHE_NR; i++)
-               INIT_LIST_HEAD(&auth->au_credcache[i]);
-       auth->au_nextgc = jiffies + (auth->au_expire >> 1);
-}
 
-/*
- * Destroy an unreferenced credential
- */
-static inline void
-rpcauth_crdestroy(struct rpc_cred *cred)
-{
-#ifdef RPC_DEBUG
-       BUG_ON(cred->cr_magic != RPCAUTH_CRED_MAGIC ||
-                       atomic_read(&cred->cr_count) ||
-                       !list_empty(&cred->cr_hash));
-       cred->cr_magic = 0;
-#endif
-       cred->cr_ops->crdestroy(cred);
+       new = kmalloc(sizeof(*new), GFP_KERNEL);
+       if (!new)
+               return -ENOMEM;
+       for (i = 0; i < RPC_CREDCACHE_NR; i++)
+               INIT_HLIST_HEAD(&new->hashtable[i]);
+       new->expire = expire;
+       new->nextgc = jiffies + (expire >> 1);
+       auth->au_credcache = new;
+       return 0;
 }
 
 /*
  * Destroy a list of credentials
  */
 static inline
-void rpcauth_destroy_credlist(struct list_head *head)
+void rpcauth_destroy_credlist(struct hlist_head *head)
 {
        struct rpc_cred *cred;
 
-       while (!list_empty(head)) {
-               cred = list_entry(head->next, struct rpc_cred, cr_hash);
-               list_del_init(&cred->cr_hash);
-               rpcauth_crdestroy(cred);
+       while (!hlist_empty(head)) {
+               cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
+               hlist_del_init(&cred->cr_hash);
+               put_rpccred(cred);
        }
 }
 
@@ -141,56 +140,56 @@ void rpcauth_destroy_credlist(struct list_head *head)
 void
 rpcauth_free_credcache(struct rpc_auth *auth)
 {
-       LIST_HEAD(free);
-       struct list_head *pos, *next;
+       struct rpc_cred_cache *cache = auth->au_credcache;
+       HLIST_HEAD(free);
+       struct hlist_node *pos, *next;
        struct rpc_cred *cred;
        int             i;
 
        spin_lock(&rpc_credcache_lock);
        for (i = 0; i < RPC_CREDCACHE_NR; i++) {
-               list_for_each_safe(pos, next, &auth->au_credcache[i]) {
-                       cred = list_entry(pos, struct rpc_cred, cr_hash);
-                       cred->cr_auth = NULL;
-                       list_del_init(&cred->cr_hash);
-                       if (atomic_read(&cred->cr_count) == 0)
-                               list_add(&cred->cr_hash, &free);
+               hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
+                       cred = hlist_entry(pos, struct rpc_cred, cr_hash);
+                       __hlist_del(&cred->cr_hash);
+                       hlist_add_head(&cred->cr_hash, &free);
                }
        }
        spin_unlock(&rpc_credcache_lock);
        rpcauth_destroy_credlist(&free);
 }
 
-static inline int
-rpcauth_prune_expired(struct rpc_cred *cred, struct list_head *free)
+static void
+rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free)
 {
-       if (atomic_read(&cred->cr_count) != 0)
-              return 0;
-       if (time_before(jiffies, cred->cr_expire))
-               return 0;
-       cred->cr_auth = NULL;
-       list_del(&cred->cr_hash);
-       list_add(&cred->cr_hash, free);
-       return 1;
+       if (atomic_read(&cred->cr_count) != 1)
+              return;
+       if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire))
+               cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+       if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) {
+               __hlist_del(&cred->cr_hash);
+               hlist_add_head(&cred->cr_hash, free);
+       }
 }
 
 /*
  * Remove stale credentials. Avoid sleeping inside the loop.
  */
 static void
-rpcauth_gc_credcache(struct rpc_auth *auth, struct list_head *free)
+rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free)
 {
-       struct list_head *pos, *next;
+       struct rpc_cred_cache *cache = auth->au_credcache;
+       struct hlist_node *pos, *next;
        struct rpc_cred *cred;
        int             i;
 
        dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
        for (i = 0; i < RPC_CREDCACHE_NR; i++) {
-               list_for_each_safe(pos, next, &auth->au_credcache[i]) {
-                       cred = list_entry(pos, struct rpc_cred, cr_hash);
-                       rpcauth_prune_expired(cred, free);
+               hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
+                       cred = hlist_entry(pos, struct rpc_cred, cr_hash);
+                       rpcauth_prune_expired(auth, cred, free);
                }
        }
-       auth->au_nextgc = jiffies + auth->au_expire;
+       cache->nextgc = jiffies + cache->expire;
 }
 
 /*
@@ -198,42 +197,39 @@ rpcauth_gc_credcache(struct rpc_auth *auth, struct list_head *free)
  */
 struct rpc_cred *
 rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
-               int taskflags)
+               int flags)
 {
-       LIST_HEAD(free);
-       struct list_head *pos, *next;
+       struct rpc_cred_cache *cache = auth->au_credcache;
+       HLIST_HEAD(free);
+       struct hlist_node *pos, *next;
        struct rpc_cred *new = NULL,
                        *cred = NULL;
        int             nr = 0;
 
-       if (!(taskflags & RPC_TASK_ROOTCREDS))
+       if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
                nr = acred->uid & RPC_CREDCACHE_MASK;
 retry:
        spin_lock(&rpc_credcache_lock);
-       if (time_before(auth->au_nextgc, jiffies))
+       if (time_before(cache->nextgc, jiffies))
                rpcauth_gc_credcache(auth, &free);
-       list_for_each_safe(pos, next, &auth->au_credcache[nr]) {
+       hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {
                struct rpc_cred *entry;
-               entry = list_entry(pos, struct rpc_cred, cr_hash);
-               if (entry->cr_flags & RPCAUTH_CRED_DEAD)
-                       continue;
-               if (rpcauth_prune_expired(entry, &free))
-                       continue;
-               if (entry->cr_ops->crmatch(acred, entry, taskflags)) {
-                       list_del(&entry->cr_hash);
+               entry = hlist_entry(pos, struct rpc_cred, cr_hash);
+               if (entry->cr_ops->crmatch(acred, entry, flags)) {
+                       hlist_del(&entry->cr_hash);
                        cred = entry;
                        break;
                }
+               rpcauth_prune_expired(auth, entry, &free);
        }
        if (new) {
                if (cred)
-                       list_add(&new->cr_hash, &free);
+                       hlist_add_head(&new->cr_hash, &free);
                else
                        cred = new;
        }
        if (cred) {
-               list_add(&cred->cr_hash, &auth->au_credcache[nr]);
-               cred->cr_auth = auth;
+               hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
                get_rpccred(cred);
        }
        spin_unlock(&rpc_credcache_lock);
@@ -241,12 +237,21 @@ retry:
        rpcauth_destroy_credlist(&free);
 
        if (!cred) {
-               new = auth->au_ops->crcreate(auth, acred, taskflags);
-               if (new) {
+               new = auth->au_ops->crcreate(auth, acred, flags);
+               if (!IS_ERR(new)) {
 #ifdef RPC_DEBUG
                        new->cr_magic = RPCAUTH_CRED_MAGIC;
 #endif
                        goto retry;
+               } else
+                       cred = new;
+       } else if ((cred->cr_flags & RPCAUTH_CRED_NEW)
+                       && cred->cr_ops->cr_init != NULL
+                       && !(flags & RPCAUTH_LOOKUP_NEW)) {
+               int res = cred->cr_ops->cr_init(auth, cred);
+               if (res < 0) {
+                       put_rpccred(cred);
+                       cred = ERR_PTR(res);
                }
        }
 
@@ -254,20 +259,21 @@ retry:
 }
 
 struct rpc_cred *
-rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
+rpcauth_lookupcred(struct rpc_auth *auth, int flags)
 {
-       struct auth_cred acred;
+       struct auth_cred acred = {
+               .uid = current->fsuid,
+               .gid = current->fsgid,
+               .tag = dx_current_tag(),
+               .group_info = current->group_info,
+       };
        struct rpc_cred *ret;
 
-       get_group_info(current->group_info);
-       acred.uid = XIDINO_UID(current->fsuid, current->xid);
-       acred.gid = XIDINO_GID(current->fsgid, current->xid);
-       acred.group_info = current->group_info;
-
        dprintk("RPC:     looking up %s cred\n",
                auth->au_ops->au_name);
-       ret = rpcauth_lookup_credcache(auth, &acred, taskflags);
-       put_group_info(current->group_info);
+       get_group_info(acred.group_info);
+       ret = auth->au_ops->lookup_cred(auth, &acred, flags);
+       put_group_info(acred.group_info);
        return ret;
 }
 
@@ -275,21 +281,26 @@ struct rpc_cred *
 rpcauth_bindcred(struct rpc_task *task)
 {
        struct rpc_auth *auth = task->tk_auth;
-       struct auth_cred acred;
+       struct auth_cred acred = {
+               .uid = current->fsuid,
+               .gid = current->fsgid,
+               .tag = dx_current_tag(),
+               .group_info = current->group_info,
+       };
        struct rpc_cred *ret;
-
-       get_group_info(current->group_info);
-       acred.uid = XIDINO_UID(current->fsuid, current->xid);
-       acred.gid = XIDINO_GID(current->fsgid, current->xid);
-       acred.group_info = current->group_info;
+       int flags = 0;
 
        dprintk("RPC: %4d looking up %s cred\n",
                task->tk_pid, task->tk_auth->au_ops->au_name);
-       task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, &acred, task->tk_flags);
-       if (task->tk_msg.rpc_cred == 0)
-               task->tk_status = -ENOMEM;
-       ret = task->tk_msg.rpc_cred;
-       put_group_info(current->group_info);
+       get_group_info(acred.group_info);
+       if (task->tk_flags & RPC_TASK_ROOTCREDS)
+               flags |= RPCAUTH_LOOKUP_ROOTCREDS;
+       ret = auth->au_ops->lookup_cred(auth, &acred, flags);
+       if (!IS_ERR(ret))
+               task->tk_msg.rpc_cred = ret;
+       else
+               task->tk_status = PTR_ERR(ret);
+       put_group_info(acred.group_info);
        return ret;
 }
 
@@ -305,65 +316,54 @@ rpcauth_holdcred(struct rpc_task *task)
 void
 put_rpccred(struct rpc_cred *cred)
 {
-       if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
+       cred->cr_expire = jiffies;
+       if (!atomic_dec_and_test(&cred->cr_count))
                return;
-
-       if ((cred->cr_flags & RPCAUTH_CRED_DEAD) && !list_empty(&cred->cr_hash))
-               list_del_init(&cred->cr_hash);
-
-       if (list_empty(&cred->cr_hash)) {
-               spin_unlock(&rpc_credcache_lock);
-               rpcauth_crdestroy(cred);
-               return;
-       }
-       cred->cr_expire = jiffies + cred->cr_auth->au_expire;
-       spin_unlock(&rpc_credcache_lock);
+       cred->cr_ops->crdestroy(cred);
 }
 
 void
 rpcauth_unbindcred(struct rpc_task *task)
 {
-       struct rpc_auth *auth = task->tk_auth;
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
 
        dprintk("RPC: %4d releasing %s cred %p\n",
-               task->tk_pid, auth->au_ops->au_name, cred);
+               task->tk_pid, task->tk_auth->au_ops->au_name, cred);
 
        put_rpccred(cred);
        task->tk_msg.rpc_cred = NULL;
 }
 
-u32 *
-rpcauth_marshcred(struct rpc_task *task, u32 *p)
+__be32 *
+rpcauth_marshcred(struct rpc_task *task, __be32 *p)
 {
-       struct rpc_auth *auth = task->tk_auth;
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
 
        dprintk("RPC: %4d marshaling %s cred %p\n",
-               task->tk_pid, auth->au_ops->au_name, cred);
-       return cred->cr_ops->crmarshal(task, p,
-                               task->tk_flags & RPC_CALL_REALUID);
+               task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+
+       return cred->cr_ops->crmarshal(task, p);
 }
 
-u32 *
-rpcauth_checkverf(struct rpc_task *task, u32 *p)
+__be32 *
+rpcauth_checkverf(struct rpc_task *task, __be32 *p)
 {
-       struct rpc_auth *auth = task->tk_auth;
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
 
        dprintk("RPC: %4d validating %s cred %p\n",
-               task->tk_pid, auth->au_ops->au_name, cred);
+               task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+
        return cred->cr_ops->crvalidate(task, p);
 }
 
 int
 rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
-               u32 *data, void *obj)
+               __be32 *data, void *obj)
 {
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
 
        dprintk("RPC: %4d using %s cred %p to wrap rpc data\n",
-                       task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
+                       task->tk_pid, cred->cr_ops->cr_name, cred);
        if (cred->cr_ops->crwrap_req)
                return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
        /* By default, we encode the arguments normally. */
@@ -372,12 +372,12 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
 
 int
 rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
-               u32 *data, void *obj)
+               __be32 *data, void *obj)
 {
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
 
        dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n",
-                       task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
+                       task->tk_pid, cred->cr_ops->cr_name, cred);
        if (cred->cr_ops->crunwrap_resp)
                return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
                                                   data, obj);
@@ -388,13 +388,16 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
 int
 rpcauth_refreshcred(struct rpc_task *task)
 {
-       struct rpc_auth *auth = task->tk_auth;
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
+       int err;
 
        dprintk("RPC: %4d refreshing %s cred %p\n",
-               task->tk_pid, auth->au_ops->au_name, cred);
-       task->tk_status = cred->cr_ops->crrefresh(task);
-       return task->tk_status;
+               task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+
+       err = cred->cr_ops->crrefresh(task);
+       if (err < 0)
+               task->tk_status = err;
+       return err;
 }
 
 void
@@ -414,10 +417,3 @@ rpcauth_uptodatecred(struct rpc_task *task)
        return !(task->tk_msg.rpc_cred) ||
                (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
 }
-
-int
-rpcauth_deadcred(struct rpc_task *task)
-{
-       return !(task->tk_msg.rpc_cred) ||
-               (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_DEAD);
-}