svcrpc: fix handling of garbage args
[pandora-kernel.git] / net / sunrpc / auth_gss / svcauth_gss.c
index 7da7050..81ae3d6 100644 (file)
@@ -224,38 +224,34 @@ static int rsi_parse(struct cache_detail *cd,
 
        /* major/minor */
        len = qword_get(&mesg, buf, mlen);
-       if (len < 0)
+       if (len <= 0)
                goto out;
-       if (len == 0) {
+       rsii.major_status = simple_strtoul(buf, &ep, 10);
+       if (*ep)
+               goto out;
+       len = qword_get(&mesg, buf, mlen);
+       if (len <= 0)
+               goto out;
+       rsii.minor_status = simple_strtoul(buf, &ep, 10);
+       if (*ep)
                goto out;
-       } else {
-               rsii.major_status = simple_strtoul(buf, &ep, 10);
-               if (*ep)
-                       goto out;
-               len = qword_get(&mesg, buf, mlen);
-               if (len <= 0)
-                       goto out;
-               rsii.minor_status = simple_strtoul(buf, &ep, 10);
-               if (*ep)
-                       goto out;
 
-               /* out_handle */
-               len = qword_get(&mesg, buf, mlen);
-               if (len < 0)
-                       goto out;
-               status = -ENOMEM;
-               if (dup_to_netobj(&rsii.out_handle, buf, len))
-                       goto out;
+       /* out_handle */
+       len = qword_get(&mesg, buf, mlen);
+       if (len < 0)
+               goto out;
+       status = -ENOMEM;
+       if (dup_to_netobj(&rsii.out_handle, buf, len))
+               goto out;
 
-               /* out_token */
-               len = qword_get(&mesg, buf, mlen);
-               status = -EINVAL;
-               if (len < 0)
-                       goto out;
-               status = -ENOMEM;
-               if (dup_to_netobj(&rsii.out_token, buf, len))
-                       goto out;
-       }
+       /* out_token */
+       len = qword_get(&mesg, buf, mlen);
+       status = -EINVAL;
+       if (len < 0)
+               goto out;
+       status = -ENOMEM;
+       if (dup_to_netobj(&rsii.out_token, buf, len))
+               goto out;
        rsii.h.expiry_time = expiry;
        rsip = rsi_update(&rsii, rsip);
        status = 0;
@@ -631,7 +627,8 @@ svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
        return 0;
 }
 
-/* Verify the checksum on the header and return SVC_OK on success.
+/*
+ * Verify the checksum on the header and return SVC_OK on success.
  * Otherwise, return SVC_DROP (in the case of a bad sequence number)
  * or return SVC_DENIED and indicate error in authp.
  */
@@ -960,6 +957,83 @@ gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip)
        return rc;
 }
 
+/*
+ * Having read the cred already and found we're in the context
+ * initiation case, read the verifier and initiate (or check the results
+ * of) upcalls to userspace for help with context initiation.  If
+ * the upcall results are available, write the verifier and result.
+ * Otherwise, drop the request pending an answer to the upcall.
+ */
+static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
+                       struct rpc_gss_wire_cred *gc, __be32 *authp)
+{
+       struct kvec *argv = &rqstp->rq_arg.head[0];
+       struct kvec *resv = &rqstp->rq_res.head[0];
+       struct xdr_netobj tmpobj;
+       struct rsi *rsip, rsikey;
+       int ret;
+
+       /* Read the verifier; should be NULL: */
+       *authp = rpc_autherr_badverf;
+       if (argv->iov_len < 2 * 4)
+               return SVC_DENIED;
+       if (svc_getnl(argv) != RPC_AUTH_NULL)
+               return SVC_DENIED;
+       if (svc_getnl(argv) != 0)
+               return SVC_DENIED;
+
+       /* Martial context handle and token for upcall: */
+       *authp = rpc_autherr_badcred;
+       if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
+               return SVC_DENIED;
+       memset(&rsikey, 0, sizeof(rsikey));
+       if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
+               return SVC_DROP;
+       *authp = rpc_autherr_badverf;
+       if (svc_safe_getnetobj(argv, &tmpobj)) {
+               kfree(rsikey.in_handle.data);
+               return SVC_DENIED;
+       }
+       if (dup_netobj(&rsikey.in_token, &tmpobj)) {
+               kfree(rsikey.in_handle.data);
+               return SVC_DROP;
+       }
+
+       /* Perform upcall, or find upcall result: */
+       rsip = rsi_lookup(&rsikey);
+       rsi_free(&rsikey);
+       if (!rsip)
+               return SVC_DROP;
+       switch (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
+       case -EAGAIN:
+       case -ETIMEDOUT:
+       case -ENOENT:
+               /* No upcall result: */
+               return SVC_DROP;
+       case 0:
+               ret = SVC_DROP;
+               /* Got an answer to the upcall; use it: */
+               if (gss_write_init_verf(rqstp, rsip))
+                       goto out;
+               if (resv->iov_len + 4 > PAGE_SIZE)
+                       goto out;
+               svc_putnl(resv, RPC_SUCCESS);
+               if (svc_safe_putnetobj(resv, &rsip->out_handle))
+                       goto out;
+               if (resv->iov_len + 3 * 4 > PAGE_SIZE)
+                       goto out;
+               svc_putnl(resv, rsip->major_status);
+               svc_putnl(resv, rsip->minor_status);
+               svc_putnl(resv, GSS_SEQ_WIN);
+               if (svc_safe_putnetobj(resv, &rsip->out_token))
+                       goto out;
+       }
+       ret = SVC_COMPLETE;
+out:
+       cache_put(&rsip->h, &rsi_cache);
+       return ret;
+}
+
 /*
  * Accept an rpcsec packet.
  * If context establishment, punt to user space
@@ -974,11 +1048,9 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
        struct kvec     *argv = &rqstp->rq_arg.head[0];
        struct kvec     *resv = &rqstp->rq_res.head[0];
        u32             crlen;
-       struct xdr_netobj tmpobj;
        struct gss_svc_data *svcdata = rqstp->rq_auth_data;
        struct rpc_gss_wire_cred *gc;
        struct rsc      *rsci = NULL;
-       struct rsi      *rsip, rsikey;
        __be32          *rpcstart;
        __be32          *reject_stat = resv->iov_base + resv->iov_len;
        int             ret;
@@ -1023,30 +1095,14 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
        if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
                goto auth_err;
 
-       /*
-        * We've successfully parsed the credential. Let's check out the
-        * verifier.  An AUTH_NULL verifier is allowed (and required) for
-        * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for
-        * PROC_DATA and PROC_DESTROY.
-        *
-        * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length).
-        * AUTH_RPCSEC_GSS verifier is:
-        *   6 (AUTH_RPCSEC_GSS), length, checksum.
-        * checksum is calculated over rpcheader from xid up to here.
-        */
        *authp = rpc_autherr_badverf;
        switch (gc->gc_proc) {
        case RPC_GSS_PROC_INIT:
        case RPC_GSS_PROC_CONTINUE_INIT:
-               if (argv->iov_len < 2 * 4)
-                       goto auth_err;
-               if (svc_getnl(argv) != RPC_AUTH_NULL)
-                       goto auth_err;
-               if (svc_getnl(argv) != 0)
-                       goto auth_err;
-               break;
+               return svcauth_gss_handle_init(rqstp, gc, authp);
        case RPC_GSS_PROC_DATA:
        case RPC_GSS_PROC_DESTROY:
+               /* Look up the context, and check the verifier: */
                *authp = rpcsec_gsserr_credproblem;
                rsci = gss_svc_searchbyctx(&gc->gc_ctx);
                if (!rsci)
@@ -1067,54 +1123,10 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
 
        /* now act upon the command: */
        switch (gc->gc_proc) {
-       case RPC_GSS_PROC_INIT:
-       case RPC_GSS_PROC_CONTINUE_INIT:
-               *authp = rpc_autherr_badcred;
-               if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
-                       goto auth_err;
-               memset(&rsikey, 0, sizeof(rsikey));
-               if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
-                       goto drop;
-               *authp = rpc_autherr_badverf;
-               if (svc_safe_getnetobj(argv, &tmpobj)) {
-                       kfree(rsikey.in_handle.data);
-                       goto auth_err;
-               }
-               if (dup_netobj(&rsikey.in_token, &tmpobj)) {
-                       kfree(rsikey.in_handle.data);
-                       goto drop;
-               }
-
-               rsip = rsi_lookup(&rsikey);
-               rsi_free(&rsikey);
-               if (!rsip) {
-                       goto drop;
-               }
-               switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
-               case -EAGAIN:
-               case -ETIMEDOUT:
-               case -ENOENT:
-                       goto drop;
-               case 0:
-                       if (gss_write_init_verf(rqstp, rsip))
-                               goto drop;
-                       if (resv->iov_len + 4 > PAGE_SIZE)
-                               goto drop;
-                       svc_putnl(resv, RPC_SUCCESS);
-                       if (svc_safe_putnetobj(resv, &rsip->out_handle))
-                               goto drop;
-                       if (resv->iov_len + 3 * 4 > PAGE_SIZE)
-                               goto drop;
-                       svc_putnl(resv, rsip->major_status);
-                       svc_putnl(resv, rsip->minor_status);
-                       svc_putnl(resv, GSS_SEQ_WIN);
-                       if (svc_safe_putnetobj(resv, &rsip->out_token))
-                               goto drop;
-               }
-               goto complete;
        case RPC_GSS_PROC_DESTROY:
                if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
                        goto auth_err;
+               rsci->h.expiry_time = get_seconds();
                set_bit(CACHE_NEGATIVE, &rsci->h.flags);
                if (resv->iov_len + 4 > PAGE_SIZE)
                        goto drop;
@@ -1132,20 +1144,20 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                case RPC_GSS_SVC_NONE:
                        break;
                case RPC_GSS_SVC_INTEGRITY:
-                       if (unwrap_integ_data(&rqstp->rq_arg,
-                                       gc->gc_seq, rsci->mechctx))
-                               goto auth_err;
                        /* placeholders for length and seq. number: */
                        svc_putnl(resv, 0);
                        svc_putnl(resv, 0);
+                       if (unwrap_integ_data(&rqstp->rq_arg,
+                                       gc->gc_seq, rsci->mechctx))
+                               goto garbage_args;
                        break;
                case RPC_GSS_SVC_PRIVACY:
-                       if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
-                                       gc->gc_seq, rsci->mechctx))
-                               goto auth_err;
                        /* placeholders for length and seq. number: */
                        svc_putnl(resv, 0);
                        svc_putnl(resv, 0);
+                       if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
+                                       gc->gc_seq, rsci->mechctx))
+                               goto garbage_args;
                        break;
                default:
                        goto auth_err;
@@ -1157,8 +1169,11 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                ret = SVC_OK;
                goto out;
        }
+garbage_args:
+       ret = SVC_GARBAGE;
+       goto out;
 auth_err:
-       /* Restore write pointer to original value: */
+       /* Restore write pointer to its original value: */
        xdr_ressize_check(rqstp, reject_stat);
        ret = SVC_DENIED;
        goto out;
@@ -1376,19 +1391,26 @@ int
 gss_svc_init(void)
 {
        int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);
-       if (rv == 0) {
-               cache_register(&rsc_cache);
-               cache_register(&rsi_cache);
-       }
+       if (rv)
+               return rv;
+       rv = cache_register(&rsc_cache);
+       if (rv)
+               goto out1;
+       rv = cache_register(&rsi_cache);
+       if (rv)
+               goto out2;
+       return 0;
+out2:
+       cache_unregister(&rsc_cache);
+out1:
+       svc_auth_unregister(RPC_AUTH_GSS);
        return rv;
 }
 
 void
 gss_svc_shutdown(void)
 {
-       if (cache_unregister(&rsc_cache))
-               printk(KERN_ERR "auth_rpcgss: failed to unregister rsc cache\n");
-       if (cache_unregister(&rsi_cache))
-               printk(KERN_ERR "auth_rpcgss: failed to unregister rsi cache\n");
+       cache_unregister(&rsc_cache);
+       cache_unregister(&rsi_cache);
        svc_auth_unregister(RPC_AUTH_GSS);
 }