rpc: track number of users of the gss upcall pipe
[safe/jmp/linux-2.6] / net / sunrpc / auth_gss / auth_gss.c
index 1f2d85e..51aa27d 100644 (file)
@@ -33,8 +33,6 @@
  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $Id$
  */
 
 
@@ -63,22 +61,11 @@ static const struct rpc_credops gss_nullops;
 # define RPCDBG_FACILITY       RPCDBG_AUTH
 #endif
 
-#define NFS_NGROUPS    16
-
-#define GSS_CRED_SLACK         1024            /* XXX: unused */
+#define GSS_CRED_SLACK         1024
 /* length of a krb5 verifier (48), plus data added before arguments when
  * using integrity (two 4-byte integers): */
 #define GSS_VERF_SLACK         100
 
-/* XXX this define must match the gssd define
-* as it is passed to gssd to signal the use of
-* machine creds should be part of the shared rpc interface */
-
-#define CA_RUN_AS_MACHINE  0x00000200
-
-/* dump the buffer in `emacs-hexl' style */
-#define isprint(c)      ((c > 0x1f) && (c < 0x7f))
-
 struct gss_auth {
        struct kref kref;
        struct rpc_auth rpc_auth;
@@ -88,6 +75,8 @@ struct gss_auth {
        struct dentry *dentry;
 };
 
+static atomic_t pipe_users = ATOMIC_INIT(0);
+
 static void gss_free_ctx(struct gss_cl_ctx *);
 static struct rpc_pipe_ops gss_upcall_ops;
 
@@ -114,27 +103,14 @@ static void
 gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
 {
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
-       struct gss_cl_ctx *old;
 
-       old = gss_cred->gc_ctx;
+       if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
+               return;
+       gss_get_ctx(ctx);
        rcu_assign_pointer(gss_cred->gc_ctx, ctx);
        set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
+       smp_mb__before_clear_bit();
        clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
-       if (old)
-               gss_put_ctx(old);
-}
-
-static int
-gss_cred_is_uptodate_ctx(struct rpc_cred *cred)
-{
-       struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
-       int res = 0;
-
-       rcu_read_lock();
-       if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) && gss_cred->gc_ctx)
-               res = 1;
-       rcu_read_unlock();
-       return res;
 }
 
 static const void *
@@ -159,7 +135,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest)
        q = (const void *)((const char *)p + len);
        if (unlikely(q > end || q < p))
                return ERR_PTR(-EFAULT);
-       dest->data = kmemdup(p, len, GFP_KERNEL);
+       dest->data = kmemdup(p, len, GFP_NOFS);
        if (unlikely(dest->data == NULL))
                return ERR_PTR(-ENOMEM);
        dest->len = len;
@@ -184,7 +160,7 @@ gss_alloc_context(void)
 {
        struct gss_cl_ctx *ctx;
 
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       ctx = kzalloc(sizeof(*ctx), GFP_NOFS);
        if (ctx != NULL) {
                ctx->gc_proc = RPC_GSS_PROC_DATA;
                ctx->gc_seq = 1;        /* NetApp 6.4R1 doesn't accept seq. no. 0 */
@@ -263,9 +239,11 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
 {
        if (!atomic_dec_and_test(&gss_msg->count))
                return;
+       atomic_dec(&pipe_users);
        BUG_ON(!list_empty(&gss_msg->list));
        if (gss_msg->ctx != NULL)
                gss_put_ctx(gss_msg->ctx);
+       rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
        kfree(gss_msg);
 }
 
@@ -284,7 +262,7 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
        return NULL;
 }
 
-/* Try to add a upcall to the pipefs queue.
+/* Try to add an upcall to the pipefs queue.
  * If an upcall owned by our uid already exists, then we return a reference
  * to that upcall instead of adding the new upcall.
  */
@@ -339,7 +317,7 @@ gss_upcall_callback(struct rpc_task *task)
 
        spin_lock(&inode->i_lock);
        if (gss_msg->ctx)
-               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx));
+               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx);
        else
                task->tk_status = gss_msg->msg.errno;
        gss_cred->gc_upcall = NULL;
@@ -353,28 +331,36 @@ gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid)
 {
        struct gss_upcall_msg *gss_msg;
 
-       gss_msg = kzalloc(sizeof(*gss_msg), GFP_KERNEL);
-       if (gss_msg != NULL) {
-               INIT_LIST_HEAD(&gss_msg->list);
-               rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
-               init_waitqueue_head(&gss_msg->waitqueue);
-               atomic_set(&gss_msg->count, 1);
-               gss_msg->msg.data = &gss_msg->uid;
-               gss_msg->msg.len = sizeof(gss_msg->uid);
-               gss_msg->uid = uid;
-               gss_msg->auth = gss_auth;
-       }
+       gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
+       if (gss_msg == NULL)
+               return ERR_PTR(-ENOMEM);
+       atomic_inc(&pipe_users);
+       INIT_LIST_HEAD(&gss_msg->list);
+       rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
+       init_waitqueue_head(&gss_msg->waitqueue);
+       atomic_set(&gss_msg->count, 1);
+       gss_msg->msg.data = &gss_msg->uid;
+       gss_msg->msg.len = sizeof(gss_msg->uid);
+       gss_msg->uid = uid;
+       gss_msg->auth = gss_auth;
        return gss_msg;
 }
 
 static struct gss_upcall_msg *
 gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred)
 {
+       struct gss_cred *gss_cred = container_of(cred,
+                       struct gss_cred, gc_base);
        struct gss_upcall_msg *gss_new, *gss_msg;
+       uid_t uid = cred->cr_uid;
 
-       gss_new = gss_alloc_msg(gss_auth, cred->cr_uid);
-       if (gss_new == NULL)
-               return ERR_PTR(-ENOMEM);
+       /* Special case: rpc.gssd assumes that uid == 0 implies machine creds */
+       if (gss_cred->gc_machine_cred != 0)
+               uid = 0;
+
+       gss_new = gss_alloc_msg(gss_auth, uid);
+       if (IS_ERR(gss_new))
+               return gss_new;
        gss_msg = gss_add_msg(gss_auth, gss_new);
        if (gss_msg == gss_new) {
                int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg);
@@ -387,6 +373,18 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
        return gss_msg;
 }
 
+static void warn_gssd(void)
+{
+       static unsigned long ratelimit;
+       unsigned long now = jiffies;
+
+       if (time_after(now, ratelimit)) {
+               printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
+                               "Please check user daemon is running.\n");
+               ratelimit = now + 15*HZ;
+       }
+}
+
 static inline int
 gss_refresh_upcall(struct rpc_task *task)
 {
@@ -408,13 +406,17 @@ gss_refresh_upcall(struct rpc_task *task)
        }
        spin_lock(&inode->i_lock);
        if (gss_cred->gc_upcall != NULL)
-               rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL, NULL);
-       else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
+               rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
+       else if (gss_msg->ctx != NULL) {
+               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx);
+               gss_cred->gc_upcall = NULL;
+               rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
+       } else if (gss_msg->msg.errno >= 0) {
                task->tk_timeout = 0;
                gss_cred->gc_upcall = gss_msg;
                /* gss_upcall_callback will release the reference to gss_upcall_msg */
                atomic_inc(&gss_msg->count);
-               rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback, NULL);
+               rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback);
        } else
                err = gss_msg->msg.errno;
        spin_unlock(&inode->i_lock);
@@ -454,7 +456,7 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
                schedule();
        }
        if (gss_msg->ctx)
-               gss_cred_set_ctx(cred, gss_get_ctx(gss_msg->ctx));
+               gss_cred_set_ctx(cred, gss_msg->ctx);
        else
                err = gss_msg->msg.errno;
        spin_unlock(&inode->i_lock);
@@ -472,16 +474,15 @@ gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
                char __user *dst, size_t buflen)
 {
        char *data = (char *)msg->data + msg->copied;
-       ssize_t mlen = msg->len;
-       ssize_t left;
+       size_t mlen = min(msg->len, buflen);
+       unsigned long left;
 
-       if (mlen > buflen)
-               mlen = buflen;
        left = copy_to_user(dst, data, mlen);
-       if (left < 0) {
-               msg->errno = left;
-               return left;
+       if (left == mlen) {
+               msg->errno = -EFAULT;
+               return -EFAULT;
        }
+
        mlen -= left;
        msg->copied += mlen;
        msg->errno = 0;
@@ -495,7 +496,6 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
 {
        const void *p, *end;
        void *buf;
-       struct rpc_clnt *clnt;
        struct gss_upcall_msg *gss_msg;
        struct inode *inode = filp->f_path.dentry->d_inode;
        struct gss_cl_ctx *ctx;
@@ -505,11 +505,10 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
        if (mlen > MSG_BUF_MAXSIZE)
                goto out;
        err = -ENOMEM;
-       buf = kmalloc(mlen, GFP_KERNEL);
+       buf = kmalloc(mlen, GFP_NOFS);
        if (!buf)
                goto out;
 
-       clnt = RPC_I(inode)->private;
        err = -EFAULT;
        if (copy_from_user(buf, src, mlen))
                goto err;
@@ -560,6 +559,13 @@ out:
        return err;
 }
 
+static int
+gss_pipe_open(struct inode *inode)
+{
+       atomic_inc(&pipe_users);
+       return 0;
+}
+
 static void
 gss_pipe_release(struct inode *inode)
 {
@@ -579,27 +585,22 @@ gss_pipe_release(struct inode *inode)
                spin_lock(&inode->i_lock);
        }
        spin_unlock(&inode->i_lock);
+
+       atomic_dec(&pipe_users);
 }
 
 static void
 gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
 {
        struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
-       static unsigned long ratelimit;
 
        if (msg->errno < 0) {
                dprintk("RPC:       gss_pipe_destroy_msg releasing msg %p\n",
                                gss_msg);
                atomic_inc(&gss_msg->count);
                gss_unhash_msg(gss_msg);
-               if (msg->errno == -ETIMEDOUT) {
-                       unsigned long now = jiffies;
-                       if (time_after(now, ratelimit)) {
-                               printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
-                                                   "Please check user daemon is running!\n");
-                               ratelimit = now + 15*HZ;
-                       }
-               }
+               if (msg->errno == -ETIMEDOUT)
+                       warn_gssd();
                gss_release_msg(gss_msg);
        }
 }
@@ -626,7 +627,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
        gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
        if (!gss_auth->mech) {
                printk(KERN_WARNING "%s: Pseudoflavor %d not found!\n",
-                               __FUNCTION__, flavor);
+                               __func__, flavor);
                goto err_free;
        }
        gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
@@ -667,7 +668,6 @@ static void
 gss_free(struct gss_auth *gss_auth)
 {
        rpc_unlink(gss_auth->dentry);
-       gss_auth->dentry = NULL;
        gss_mech_put(gss_auth->mech);
 
        kfree(gss_auth);
@@ -710,7 +710,7 @@ gss_destroying_context(struct rpc_cred *cred)
        struct rpc_task *task;
 
        if (gss_cred->gc_ctx == NULL ||
-                       gss_cred->gc_ctx->gc_proc == RPC_GSS_PROC_DESTROY)
+           test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
                return 0;
 
        gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY;
@@ -720,7 +720,7 @@ gss_destroying_context(struct rpc_cred *cred)
         * by the RPC call or by the put_rpccred() below */
        get_rpccred(cred);
 
-       task = rpc_call_null(gss_auth->client, cred, RPC_TASK_ASYNC);
+       task = rpc_call_null(gss_auth->client, cred, RPC_TASK_ASYNC|RPC_TASK_SOFT);
        if (!IS_ERR(task))
                rpc_put_task(task);
 
@@ -774,14 +774,12 @@ gss_free_cred_callback(struct rcu_head *head)
 }
 
 static void
-gss_destroy_cred(struct rpc_cred *cred)
+gss_destroy_nullcred(struct rpc_cred *cred)
 {
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
        struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
        struct gss_cl_ctx *ctx = gss_cred->gc_ctx;
 
-       if (gss_destroying_context(cred))
-               return;
        rcu_assign_pointer(gss_cred->gc_ctx, NULL);
        call_rcu(&cred->cr_rcu, gss_free_cred_callback);
        if (ctx)
@@ -789,6 +787,15 @@ gss_destroy_cred(struct rpc_cred *cred)
        kref_put(&gss_auth->kref, gss_free_callback);
 }
 
+static void
+gss_destroy_cred(struct rpc_cred *cred)
+{
+
+       if (gss_destroying_context(cred))
+               return;
+       gss_destroy_nullcred(cred);
+}
+
 /*
  * Lookup RPCSEC_GSS cred for the current process
  */
@@ -808,7 +815,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
        dprintk("RPC:       gss_create_cred for uid %d, flavor %d\n",
                acred->uid, auth->au_flavor);
 
-       if (!(cred = kzalloc(sizeof(*cred), GFP_KERNEL)))
+       if (!(cred = kzalloc(sizeof(*cred), GFP_NOFS)))
                goto out_err;
 
        rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
@@ -818,6 +825,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
         */
        cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW;
        cred->gc_service = gss_auth->service;
+       cred->gc_machine_cred = acred->machine_cred;
        kref_get(&gss_auth->kref);
        return &cred->gc_base;
 
@@ -844,17 +852,16 @@ gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
 {
        struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
 
-       /*
-        * If the searchflags have set RPCAUTH_LOOKUP_NEW, then
-        * we don't really care if the credential has expired or not,
-        * since the caller should be prepared to reinitialise it.
-        */
-       if ((flags & RPCAUTH_LOOKUP_NEW) && test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
+       if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
                goto out;
        /* Don't match with creds that have expired. */
-       if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry))
+       if (time_after(jiffies, gss_cred->gc_ctx->gc_expiry))
+               return 0;
+       if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags))
                return 0;
 out:
+       if (acred->machine_cred != gss_cred->gc_machine_cred)
+               return 0;
        return (rc->cr_uid == acred->uid);
 }
 
@@ -918,16 +925,48 @@ out_put_ctx:
        return NULL;
 }
 
+static int gss_renew_cred(struct rpc_task *task)
+{
+       struct rpc_cred *oldcred = task->tk_msg.rpc_cred;
+       struct gss_cred *gss_cred = container_of(oldcred,
+                                                struct gss_cred,
+                                                gc_base);
+       struct rpc_auth *auth = oldcred->cr_auth;
+       struct auth_cred acred = {
+               .uid = oldcred->cr_uid,
+               .machine_cred = gss_cred->gc_machine_cred,
+       };
+       struct rpc_cred *new;
+
+       new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW);
+       if (IS_ERR(new))
+               return PTR_ERR(new);
+       task->tk_msg.rpc_cred = new;
+       put_rpccred(oldcred);
+       return 0;
+}
+
 /*
 * Refresh credentials. XXX - finish
 */
 static int
 gss_refresh(struct rpc_task *task)
 {
+       struct rpc_cred *cred = task->tk_msg.rpc_cred;
+       int ret = 0;
+
+       if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
+                       !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) {
+               ret = gss_renew_cred(task);
+               if (ret < 0)
+                       goto out;
+               cred = task->tk_msg.rpc_cred;
+       }
 
-       if (!gss_cred_is_uptodate_ctx(task->tk_msg.rpc_cred))
-               return gss_refresh_upcall(task);
-       return 0;
+       if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
+               ret = gss_refresh_upcall(task);
+out:
+       return ret;
 }
 
 /* Dummy refresh routine: used only when destroying the context */
@@ -1002,7 +1041,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
        *p++ = htonl(rqstp->rq_seqno);
 
-       status = rpc_call_xdrproc(encode, rqstp, p, obj);
+       status = encode(rqstp, p, obj);
        if (status)
                return status;
 
@@ -1096,7 +1135,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
        *p++ = htonl(rqstp->rq_seqno);
 
-       status = rpc_call_xdrproc(encode, rqstp, p, obj);
+       status = encode(rqstp, p, obj);
        if (status)
                return status;
 
@@ -1155,12 +1194,12 @@ gss_wrap_req(struct rpc_task *task,
                /* The spec seems a little ambiguous here, but I think that not
                 * wrapping context destruction requests makes the most sense.
                 */
-               status = rpc_call_xdrproc(encode, rqstp, p, obj);
+               status = encode(rqstp, p, obj);
                goto out;
        }
        switch (gss_cred->gc_service) {
                case RPC_GSS_SVC_NONE:
-                       status = rpc_call_xdrproc(encode, rqstp, p, obj);
+                       status = encode(rqstp, p, obj);
                        break;
                case RPC_GSS_SVC_INTEGRITY:
                        status = gss_wrap_req_integ(cred, ctx, encode,
@@ -1276,7 +1315,7 @@ gss_unwrap_resp(struct rpc_task *task,
        cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
                                                + (savedlen - head->iov_len);
 out_decode:
-       status = rpc_call_xdrproc(decode, rqstp, p, obj);
+       status = decode(rqstp, p, obj);
 out:
        gss_put_ctx(ctx);
        dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
@@ -1287,9 +1326,7 @@ out:
 static const struct rpc_authops authgss_ops = {
        .owner          = THIS_MODULE,
        .au_flavor      = RPC_AUTH_GSS,
-#ifdef RPC_DEBUG
        .au_name        = "RPCSEC_GSS",
-#endif
        .create         = gss_create,
        .destroy        = gss_destroy,
        .lookup_cred    = gss_lookup_cred,
@@ -1300,6 +1337,7 @@ static const struct rpc_credops gss_credops = {
        .cr_name        = "AUTH_GSS",
        .crdestroy      = gss_destroy_cred,
        .cr_init        = gss_cred_init,
+       .crbind         = rpcauth_generic_bind_cred,
        .crmatch        = gss_match,
        .crmarshal      = gss_marshal,
        .crrefresh      = gss_refresh,
@@ -1310,7 +1348,8 @@ static const struct rpc_credops gss_credops = {
 
 static const struct rpc_credops gss_nullops = {
        .cr_name        = "AUTH_GSS",
-       .crdestroy      = gss_destroy_cred,
+       .crdestroy      = gss_destroy_nullcred,
+       .crbind         = rpcauth_generic_bind_cred,
        .crmatch        = gss_match,
        .crmarshal      = gss_marshal,
        .crrefresh      = gss_refresh_null,
@@ -1323,6 +1362,7 @@ static struct rpc_pipe_ops gss_upcall_ops = {
        .upcall         = gss_pipe_upcall,
        .downcall       = gss_pipe_downcall,
        .destroy_msg    = gss_pipe_destroy_msg,
+       .open_pipe      = gss_pipe_open,
        .release_pipe   = gss_pipe_release,
 };