svcrpc: explicitly reject compounds that are not padded out to 4-byte multiple
[pandora-kernel.git] / fs / nfsd / nfs4xdr.c
index ee7237f..93b50ba 100644 (file)
@@ -103,11 +103,6 @@ xdr_error:                                 \
        (x) = (u64)ntohl(*p++) << 32;           \
        (x) |= ntohl(*p++);                     \
 } while (0)
-#define READTIME(x)       do {                 \
-       p++;                                    \
-       (x) = ntohl(*p++);                      \
-       p++;                                    \
-} while (0)
 #define READMEM(x,nbytes) do {                 \
        x = (char *)p;                          \
        p += XDR_QUADLEN(nbytes);               \
@@ -190,6 +185,15 @@ static int zero_clientid(clientid_t *clid)
        return (clid->cl_boot == 0) && (clid->cl_id == 0);
 }
 
+/**
+ * defer_free - mark an allocation as deferred freed
+ * @argp: NFSv4 compound argument structure to be freed with
+ * @release: release callback to free @p, typically kfree()
+ * @p: pointer to be freed
+ *
+ * Marks @p to be freed when processing the compound operation
+ * described in @argp finishes.
+ */
 static int
 defer_free(struct nfsd4_compoundargs *argp,
                void (*release)(const void *), void *p)
@@ -206,6 +210,16 @@ defer_free(struct nfsd4_compoundargs *argp,
        return 0;
 }
 
+/**
+ * savemem - duplicate a chunk of memory for later processing
+ * @argp: NFSv4 compound argument structure to be freed with
+ * @p: pointer to be duplicated
+ * @nbytes: length to be duplicated
+ *
+ * Returns a pointer to a copy of @nbytes bytes of memory at @p
+ * that are preserved until processing of the NFSv4 compound
+ * operation described by @argp finishes.
+ */
 static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
 {
        if (p == argp->tmp) {
@@ -257,7 +271,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
        int expected_len, len = 0;
        u32 dummy32;
        char *buf;
-       int host_err;
 
        DECODE_HEAD;
        iattr->ia_valid = 0;
@@ -281,13 +294,12 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                READ32(nace);
 
                if (nace > NFS4_ACL_MAX)
-                       return nfserr_resource;
+                       return nfserr_fbig;
 
                *acl = nfs4_acl_new(nace);
-               if (*acl == NULL) {
-                       host_err = -ENOMEM;
-                       goto out_nfserr;
-               }
+               if (*acl == NULL)
+                       return nfserr_jukebox;
+
                defer_free(argp, kfree, *acl);
 
                (*acl)->naces = nace;
@@ -425,10 +437,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                goto xdr_error;
 
        DECODE_TAIL;
-
-out_nfserr:
-       status = nfserrno(host_err);
-       goto out;
 }
 
 static __be32
@@ -1214,7 +1222,6 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
        }
        write->wr_head.iov_base = p;
        write->wr_head.iov_len = avail;
-       WARN_ON(avail != (XDR_QUADLEN(avail) << 2));
        write->wr_pagelist = argp->pagelist;
 
        len = XDR_QUADLEN(write->wr_buflen) << 2;
@@ -1957,56 +1964,16 @@ static u32 nfs4_file_type(umode_t mode)
        };
 }
 
-static __be32
-nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, kuid_t uid, kgid_t gid,
-                       __be32 **p, int *buflen)
-{
-       int status;
-
-       if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
-               return nfserr_resource;
-       if (whotype != NFS4_ACL_WHO_NAMED)
-               status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
-       else if (gid_valid(gid))
-               status = nfsd_map_gid_to_name(rqstp, gid, (u8 *)(*p + 1));
-       else
-               status = nfsd_map_uid_to_name(rqstp, uid, (u8 *)(*p + 1));
-       if (status < 0)
-               return nfserrno(status);
-       *p = xdr_encode_opaque(*p, NULL, status);
-       *buflen -= (XDR_QUADLEN(status) << 2) + 4;
-       BUG_ON(*buflen < 0);
-       return 0;
-}
-
-static inline __be32
-nfsd4_encode_user(struct svc_rqst *rqstp, kuid_t user, __be32 **p, int *buflen)
-{
-       return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, user, INVALID_GID,
-                                p, buflen);
-}
-
-static inline __be32
-nfsd4_encode_group(struct svc_rqst *rqstp, kgid_t group, __be32 **p, int *buflen)
-{
-       return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, INVALID_UID, group,
-                                p, buflen);
-}
-
 static inline __be32
 nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
                __be32 **p, int *buflen)
 {
-       kuid_t uid = INVALID_UID;
-       kgid_t gid = INVALID_GID;
-
-       if (ace->whotype == NFS4_ACL_WHO_NAMED) {
-               if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
-                       gid = ace->who_gid;
-               else
-                       uid = ace->who_uid;
-       }
-       return nfsd4_encode_name(rqstp, ace->whotype, uid, gid, p, buflen);
+       if (ace->whotype != NFS4_ACL_WHO_NAMED)
+               return nfs4_acl_write_who(ace->whotype, p, buflen);
+       else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
+               return nfsd4_encode_group(rqstp, ace->who_gid, p, buflen);
+       else
+               return nfsd4_encode_user(rqstp, ace->who_uid, p, buflen);
 }
 
 #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
@@ -2090,7 +2057,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        u32 bmval1 = bmval[1];
        u32 bmval2 = bmval[2];
        struct kstat stat;
-       struct svc_fh tempfh;
+       struct svc_fh *tempfh = NULL;
        struct kstatfs statfs;
        int buflen = count << 2;
        __be32 *attrlenp;
@@ -2137,11 +2104,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                        goto out_nfserr;
        }
        if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
-               fh_init(&tempfh, NFS4_FHSIZE);
-               status = fh_compose(&tempfh, exp, dentry, NULL);
+               tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
+               status = nfserr_jukebox;
+               if (!tempfh)
+                       goto out;
+               fh_init(tempfh, NFS4_FHSIZE);
+               status = fh_compose(tempfh, exp, dentry, NULL);
                if (status)
                        goto out;
-               fhp = &tempfh;
+               fhp = tempfh;
        }
        if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
                        | FATTR4_WORD0_SUPPORTED_ATTRS)) {
@@ -2222,8 +2193,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                if ((buflen -= 4) < 0)
                        goto out_resource;
                dummy = nfs4_file_type(stat.mode);
-               if (dummy == NF4BAD)
-                       goto out_serverfault;
+               if (dummy == NF4BAD) {
+                       status = nfserr_serverfault;
+                       goto out;
+               }
                WRITE32(dummy);
        }
        if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
@@ -2317,8 +2290,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                        WRITE32(ace->flag);
                        WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
                        status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen);
-                       if (status == nfserr_resource)
-                               goto out_resource;
                        if (status)
                                goto out;
                }
@@ -2379,8 +2350,6 @@ out_acl:
        }
        if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
                status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
-               if (status == nfserr_resource)
-                       goto out_resource;
                if (status)
                        goto out;
        }
@@ -2431,15 +2400,11 @@ out_acl:
        }
        if (bmval1 & FATTR4_WORD1_OWNER) {
                status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
-               if (status == nfserr_resource)
-                       goto out_resource;
                if (status)
                        goto out;
        }
        if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
                status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
-               if (status == nfserr_resource)
-                       goto out_resource;
                if (status)
                        goto out;
        }
@@ -2517,6 +2482,8 @@ out_acl:
                        goto out;
        }
        if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
+               if ((buflen -= 16) < 0)
+                       goto out_resource;
                WRITE32(3);
                WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
                WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
@@ -2533,8 +2500,8 @@ out:
                security_release_secctx(context, contextlen);
 #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
        kfree(acl);
-       if (fhp == &tempfh)
-               fh_put(&tempfh);
+       if (tempfh)
+               fh_put(tempfh);
        return status;
 out_nfserr:
        status = nfserrno(err);
@@ -2542,9 +2509,6 @@ out_nfserr:
 out_resource:
        status = nfserr_resource;
        goto out;
-out_serverfault:
-       status = nfserr_serverfault;
-       goto out;
 }
 
 static inline int attributes_need_mount(u32 *bmval)
@@ -2621,17 +2585,14 @@ out_put:
 static __be32 *
 nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr)
 {
-       __be32 *attrlenp;
-
        if (buflen < 6)
                return NULL;
        *p++ = htonl(2);
        *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
        *p++ = htonl(0);                         /* bmval1 */
 
-       attrlenp = p++;
+       *p++ = htonl(4);     /* attribute length */
        *p++ = nfserr;       /* no htonl */
-       *attrlenp = htonl((char *)p - (char *)attrlenp - 4);
        return p;
 }
 
@@ -3244,7 +3205,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
 
                if (rpcauth_get_gssinfo(pf, &info) == 0) {
                        supported++;
-                       RESERVE_SPACE(4 + 4 + info.oid.len + 4 + 4);
+                       RESERVE_SPACE(4 + 4 + XDR_LEN(info.oid.len) + 4 + 4);
                        WRITE32(RPC_AUTH_GSS);
                        WRITE32(info.oid.len);
                        WRITEMEM(info.oid.data, info.oid.len);
@@ -3379,35 +3340,43 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                8 /* eir_clientid */ +
                4 /* eir_sequenceid */ +
                4 /* eir_flags */ +
-               4 /* spr_how */ +
-               8 /* spo_must_enforce, spo_must_allow */ +
-               8 /* so_minor_id */ +
-               4 /* so_major_id.len */ +
-               (XDR_QUADLEN(major_id_sz) * 4) +
-               4 /* eir_server_scope.len */ +
-               (XDR_QUADLEN(server_scope_sz) * 4) +
-               4 /* eir_server_impl_id.count (0) */);
+               4 /* spr_how */);
 
        WRITEMEM(&exid->clientid, 8);
        WRITE32(exid->seqid);
        WRITE32(exid->flags);
 
        WRITE32(exid->spa_how);
+       ADJUST_ARGS();
+
        switch (exid->spa_how) {
        case SP4_NONE:
                break;
        case SP4_MACH_CRED:
+               /* spo_must_enforce, spo_must_allow */
+               RESERVE_SPACE(16);
+
                /* spo_must_enforce bitmap: */
                WRITE32(2);
                WRITE32(nfs4_minimal_spo_must_enforce[0]);
                WRITE32(nfs4_minimal_spo_must_enforce[1]);
                /* empty spo_must_allow bitmap: */
                WRITE32(0);
+
+               ADJUST_ARGS();
                break;
        default:
                WARN_ON_ONCE(1);
        }
 
+       RESERVE_SPACE(
+               8 /* so_minor_id */ +
+               4 /* so_major_id.len */ +
+               (XDR_QUADLEN(major_id_sz) * 4) +
+               4 /* eir_server_scope.len */ +
+               (XDR_QUADLEN(server_scope_sz) * 4) +
+               4 /* eir_server_impl_id.count (0) */);
+
        /* The server_owner struct */
        WRITE64(minor_id);      /* Minor id */
        /* major id */
@@ -3473,28 +3442,6 @@ nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
        return 0;
 }
 
-static __be32
-nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, __be32 nfserr,
-                            struct nfsd4_destroy_session *destroy_session)
-{
-       return nfserr;
-}
-
-static __be32
-nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
-                         struct nfsd4_free_stateid *free_stateid)
-{
-       __be32 *p;
-
-       if (nfserr)
-               return nfserr;
-
-       RESERVE_SPACE(4);
-       *p++ = nfserr;
-       ADJUST_ARGS();
-       return nfserr;
-}
-
 static __be32
 nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
                      struct nfsd4_sequence *seq)
@@ -3525,6 +3472,9 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
        struct nfsd4_test_stateid_id *stateid, *next;
        __be32 *p;
 
+       if (nfserr)
+               return nfserr;
+
        RESERVE_SPACE(4 + (4 * test_stateid->ts_num_ids));
        *p++ = htonl(test_stateid->ts_num_ids);
 
@@ -3593,8 +3543,8 @@ static nfsd4_enc nfsd4_enc_ops[] = {
        [OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session,
        [OP_EXCHANGE_ID]        = (nfsd4_enc)nfsd4_encode_exchange_id,
        [OP_CREATE_SESSION]     = (nfsd4_enc)nfsd4_encode_create_session,
-       [OP_DESTROY_SESSION]    = (nfsd4_enc)nfsd4_encode_destroy_session,
-       [OP_FREE_STATEID]       = (nfsd4_enc)nfsd4_encode_free_stateid,
+       [OP_DESTROY_SESSION]    = (nfsd4_enc)nfsd4_encode_noop,
+       [OP_FREE_STATEID]       = (nfsd4_enc)nfsd4_encode_noop,
        [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
        [OP_GETDEVICEINFO]      = (nfsd4_enc)nfsd4_encode_noop,
        [OP_GETDEVICELIST]      = (nfsd4_enc)nfsd4_encode_noop,
@@ -3745,6 +3695,12 @@ int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp)
 int
 nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
 {
+       if (rqstp->rq_arg.head[0].iov_len % 4) {
+               /* client is nuts */
+               dprintk("%s: compound not properly padded! (peeraddr=%pISc xid=0x%x)",
+                       __func__, svc_addr(rqstp), be32_to_cpu(rqstp->rq_xid));
+               return 0;
+       }
        args->p = p;
        args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
        args->pagelist = rqstp->rq_arg.pages;