Merge git://oss.sgi.com:8090/xfs/xfs-2.6
[linux-drm-fsl-dcu.git] / net / sunrpc / auth_gss / svcauth_gss.c
index 638c0b576203bab4fac5c0037cf1881e2c1c32e3..8fde38ecaf211e9e4217007609aa34901eaed25d 100644 (file)
@@ -113,9 +113,7 @@ static int rsi_match(struct cache_head *a, struct cache_head *b)
 static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len)
 {
        dst->len = len;
-       dst->data = (len ? kmalloc(len, GFP_KERNEL) : NULL);
-       if (dst->data)
-               memcpy(dst->data, src, len);
+       dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL);
        if (len && !dst->data)
                return -ENOMEM;
        return 0;
@@ -174,8 +172,8 @@ static struct cache_head *rsi_alloc(void)
 }
 
 static void rsi_request(struct cache_detail *cd,
-                       struct cache_head *h,
-                       char **bpp, int *blen)
+                      struct cache_head *h,
+                      char **bpp, int *blen)
 {
        struct rsi *rsii = container_of(h, struct rsi, h);
 
@@ -186,7 +184,7 @@ static void rsi_request(struct cache_detail *cd,
 
 
 static int rsi_parse(struct cache_detail *cd,
-                    char *mesg, int mlen)
+                   char *mesg, int mlen)
 {
        /* context token expiry major minor context token */
        char *buf = mesg;
@@ -756,10 +754,9 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
        if (!new)
                goto out;
        kref_init(&new->h.ref);
-       new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+       new->h.name = kstrdup(name, GFP_KERNEL);
        if (!new->h.name)
                goto out_free_dom;
-       strcpy(new->h.name, name);
        new->h.flavour = &svcauthops_gss;
        new->pseudoflavor = pseudoflavor;
 
@@ -807,19 +804,19 @@ unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
 
        integ_len = svc_getnl(&buf->head[0]);
        if (integ_len & 3)
-               goto out;
+               return stat;
        if (integ_len > buf->len)
-               goto out;
+               return stat;
        if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
                BUG();
        /* copy out mic... */
        if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
                BUG();
        if (mic.len > RPC_MAX_AUTH_SIZE)
-               goto out;
+               return stat;
        mic.data = kmalloc(mic.len, GFP_KERNEL);
        if (!mic.data)
-               goto out;
+               return stat;
        if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
                goto out;
        maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
@@ -829,6 +826,7 @@ unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
                goto out;
        stat = 0;
 out:
+       kfree(mic.data);
        return stat;
 }
 
@@ -903,9 +901,9 @@ out_seq:
 struct gss_svc_data {
        /* decoded gss client cred: */
        struct rpc_gss_wire_cred        clcred;
-       /* pointer to the beginning of the procedure-specific results,
-        * which may be encrypted/checksummed in svcauth_gss_release: */
-       __be32                          *body_start;
+       /* save a pointer to the beginning of the encoded verifier,
+        * for use in encryption/checksumming in svcauth_gss_release: */
+       __be32                          *verf_start;
        struct rsc                      *rsci;
 };
 
@@ -968,7 +966,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
        if (!svcdata)
                goto auth_err;
        rqstp->rq_auth_data = svcdata;
-       svcdata->body_start = NULL;
+       svcdata->verf_start = NULL;
        svcdata->rsci = NULL;
        gc = &svcdata->clcred;
 
@@ -1068,7 +1066,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                }
                switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
                case -EAGAIN:
-                       goto drop;
+               case -ETIMEDOUT:
                case -ENOENT:
                        goto drop;
                case 0:
@@ -1097,6 +1095,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                goto complete;
        case RPC_GSS_PROC_DATA:
                *authp = rpcsec_gsserr_ctxproblem;
+               svcdata->verf_start = resv->iov_base + resv->iov_len;
                if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
                        goto auth_err;
                rqstp->rq_cred = rsci->cred;
@@ -1110,7 +1109,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                                        gc->gc_seq, rsci->mechctx))
                                goto auth_err;
                        /* placeholders for length and seq. number: */
-                       svcdata->body_start = resv->iov_base + resv->iov_len;
                        svc_putnl(resv, 0);
                        svc_putnl(resv, 0);
                        break;
@@ -1119,7 +1117,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                                        gc->gc_seq, rsci->mechctx))
                                goto auth_err;
                        /* placeholders for length and seq. number: */
-                       svcdata->body_start = resv->iov_base + resv->iov_len;
                        svc_putnl(resv, 0);
                        svc_putnl(resv, 0);
                        break;
@@ -1147,6 +1144,33 @@ out:
        return ret;
 }
 
+static __be32 *
+svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd)
+{
+       __be32 *p;
+       u32 verf_len;
+
+       p = gsd->verf_start;
+       gsd->verf_start = NULL;
+
+       /* If the reply stat is nonzero, don't wrap: */
+       if (*(p-1) != rpc_success)
+               return NULL;
+       /* Skip the verifier: */
+       p += 1;
+       verf_len = ntohl(*p++);
+       p += XDR_QUADLEN(verf_len);
+       /* move accept_stat to right place: */
+       memcpy(p, p + 2, 4);
+       /* Also don't wrap if the accept stat is nonzero: */
+       if (*p != rpc_success) {
+               resbuf->head[0].iov_len -= 2 * 4;
+               return NULL;
+       }
+       p++;
+       return p;
+}
+
 static inline int
 svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
 {
@@ -1160,17 +1184,9 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
        int integ_offset, integ_len;
        int stat = -EINVAL;
 
-       p = gsd->body_start;
-       gsd->body_start = NULL;
-       /* move accept_stat to right place: */
-       memcpy(p, p + 2, 4);
-       /* Don't wrap in failure case: */
-       /* Counting on not getting here if call was not even accepted! */
-       if (*p != rpc_success) {
-               resbuf->head[0].iov_len -= 2 * 4;
+       p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
+       if (p == NULL)
                goto out;
-       }
-       p++;
        integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
        integ_len = resbuf->len - integ_offset;
        BUG_ON(integ_len % 4);
@@ -1191,7 +1207,6 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
                resbuf->tail[0].iov_base = resbuf->head[0].iov_base
                                                + resbuf->head[0].iov_len;
                resbuf->tail[0].iov_len = 0;
-               rqstp->rq_restailpage = 0;
                resv = &resbuf->tail[0];
        } else {
                resv = &resbuf->tail[0];
@@ -1223,24 +1238,16 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
        int offset;
        int pad;
 
-       p = gsd->body_start;
-       gsd->body_start = NULL;
-       /* move accept_stat to right place: */
-       memcpy(p, p + 2, 4);
-       /* Don't wrap in failure case: */
-       /* Counting on not getting here if call was not even accepted! */
-       if (*p != rpc_success) {
-               resbuf->head[0].iov_len -= 2 * 4;
+       p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
+       if (p == NULL)
                return 0;
-       }
-       p++;
        len = p++;
        offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base;
        *p++ = htonl(gc->gc_seq);
        inpages = resbuf->pages;
        /* XXX: Would be better to write some xdr helper functions for
         * nfs{2,3,4}xdr.c that place the data right, instead of copying: */
-       if (resbuf->tail[0].iov_base && rqstp->rq_restailpage == 0) {
+       if (resbuf->tail[0].iov_base) {
                BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base
                                                        + PAGE_SIZE);
                BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base);
@@ -1258,7 +1265,6 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
                resbuf->tail[0].iov_base = resbuf->head[0].iov_base
                        + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE;
                resbuf->tail[0].iov_len = 0;
-               rqstp->rq_restailpage = 0;
        }
        if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages))
                return -ENOMEM;
@@ -1282,7 +1288,7 @@ svcauth_gss_release(struct svc_rqst *rqstp)
        if (gc->gc_proc != RPC_GSS_PROC_DATA)
                goto out;
        /* Release can be called twice, but we only wrap once. */
-       if (gsd->body_start == NULL)
+       if (gsd->verf_start == NULL)
                goto out;
        /* normally not set till svc_send, but we need it here: */
        /* XXX: what for?  Do we mess it up the moment we call svc_putu32