Merge tag 'nfs-for-5.7-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[platform/kernel/linux-starfive.git] / net / sunrpc / auth_gss / auth_gss.c
index ee060d5..25fbd8d 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
 #include <linux/sunrpc/auth_gss.h>
+#include <linux/sunrpc/gss_krb5.h>
 #include <linux/sunrpc/svcauth_gss.h>
 #include <linux/sunrpc/gss_err.h>
 #include <linux/workqueue.h>
@@ -1050,7 +1051,7 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
                goto err_put_mech;
        auth = &gss_auth->rpc_auth;
        auth->au_cslack = GSS_CRED_SLACK >> 2;
-       auth->au_rslack = GSS_VERF_SLACK >> 2;
+       auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2;
        auth->au_verfsize = GSS_VERF_SLACK >> 2;
        auth->au_ralign = GSS_VERF_SLACK >> 2;
        auth->au_flags = 0;
@@ -1724,8 +1725,9 @@ bad_mic:
        goto out;
 }
 
-static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
-                             struct rpc_task *task, struct xdr_stream *xdr)
+static noinline_for_stack int
+gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
+                  struct rpc_task *task, struct xdr_stream *xdr)
 {
        struct rpc_rqst *rqstp = task->tk_rqstp;
        struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf;
@@ -1816,8 +1818,9 @@ out:
        return -EAGAIN;
 }
 
-static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
-                            struct rpc_task *task, struct xdr_stream *xdr)
+static noinline_for_stack int
+gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
+                 struct rpc_task *task, struct xdr_stream *xdr)
 {
        struct rpc_rqst *rqstp = task->tk_rqstp;
        struct xdr_buf  *snd_buf = &rqstp->rq_snd_buf;
@@ -1934,35 +1937,69 @@ gss_unwrap_resp_auth(struct rpc_cred *cred)
        return 0;
 }
 
-static int
+/*
+ * RFC 2203, Section 5.3.2.2
+ *
+ *     struct rpc_gss_integ_data {
+ *             opaque databody_integ<>;
+ *             opaque checksum<>;
+ *     };
+ *
+ *     struct rpc_gss_data_t {
+ *             unsigned int seq_num;
+ *             proc_req_arg_t arg;
+ *     };
+ */
+static noinline_for_stack int
 gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
                      struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
                      struct xdr_stream *xdr)
 {
-       struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf;
-       u32 data_offset, mic_offset, integ_len, maj_stat;
+       struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf;
        struct rpc_auth *auth = cred->cr_auth;
+       u32 len, offset, seqno, maj_stat;
        struct xdr_netobj mic;
-       __be32 *p;
+       int ret;
 
-       p = xdr_inline_decode(xdr, 2 * sizeof(*p));
-       if (unlikely(!p))
+       ret = -EIO;
+       mic.data = NULL;
+
+       /* opaque databody_integ<>; */
+       if (xdr_stream_decode_u32(xdr, &len))
                goto unwrap_failed;
-       integ_len = be32_to_cpup(p++);
-       if (integ_len & 3)
+       if (len & 3)
                goto unwrap_failed;
-       data_offset = (u8 *)(p) - (u8 *)rcv_buf->head[0].iov_base;
-       mic_offset = integ_len + data_offset;
-       if (mic_offset > rcv_buf->len)
+       offset = rcv_buf->len - xdr_stream_remaining(xdr);
+       if (xdr_stream_decode_u32(xdr, &seqno))
                goto unwrap_failed;
-       if (be32_to_cpup(p) != rqstp->rq_seqno)
+       if (seqno != rqstp->rq_seqno)
                goto bad_seqno;
+       if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len))
+               goto unwrap_failed;
 
-       if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, integ_len))
+       /*
+        * The xdr_stream now points to the beginning of the
+        * upper layer payload, to be passed below to
+        * rpcauth_unwrap_resp_decode(). The checksum, which
+        * follows the upper layer payload in @rcv_buf, is
+        * located and parsed without updating the xdr_stream.
+        */
+
+       /* opaque checksum<>; */
+       offset += len;
+       if (xdr_decode_word(rcv_buf, offset, &len))
+               goto unwrap_failed;
+       offset += sizeof(__be32);
+       if (offset + len > rcv_buf->len)
                goto unwrap_failed;
-       if (xdr_buf_read_mic(rcv_buf, &mic, mic_offset))
+       mic.len = len;
+       mic.data = kmalloc(len, GFP_NOFS);
+       if (!mic.data)
+               goto unwrap_failed;
+       if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len))
                goto unwrap_failed;
-       maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
+
+       maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic);
        if (maj_stat == GSS_S_CONTEXT_EXPIRED)
                clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
        if (maj_stat != GSS_S_COMPLETE)
@@ -1970,19 +2007,24 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
 
        auth->au_rslack = auth->au_verfsize + 2 + 1 + XDR_QUADLEN(mic.len);
        auth->au_ralign = auth->au_verfsize + 2;
-       return 0;
+       ret = 0;
+
+out:
+       kfree(mic.data);
+       return ret;
+
 unwrap_failed:
        trace_rpcgss_unwrap_failed(task);
-       return -EIO;
+       goto out;
 bad_seqno:
-       trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(p));
-       return -EIO;
+       trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno);
+       goto out;
 bad_mic:
        trace_rpcgss_verify_mic(task, maj_stat);
-       return -EIO;
+       goto out;
 }
 
-static int
+static noinline_for_stack int
 gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
                     struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
                     struct xdr_stream *xdr)