diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2023-01-02 12:06:35 -0500 |
---|---|---|
committer | Chuck Lever <chuck.lever@oracle.com> | 2023-02-20 09:20:15 -0500 |
commit | c020fa695af64093acf689beaa39d9e956a55f23 (patch) | |
tree | f894f0b5b1b0f003b889f2943e7f910626dcaf6a /net/sunrpc/auth_gss | |
parent | 1cbfb921978f98d8b3cdd04b3b2a98e7f4e0fcef (diff) |
SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream
The entire RPC_GSS_PROC_INIT path is converted over to xdr_stream
for decoding the Call credential and verifier.
Done as part of hardening the server-side RPC header decoding path.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Diffstat (limited to 'net/sunrpc/auth_gss')
-rw-r--r-- | net/sunrpc/auth_gss/svcauth_gss.c | 87 |
1 files changed, 58 insertions, 29 deletions
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 43243d8e59bf..19040145acd0 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1112,39 +1112,43 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, struct xdr_netobj *in_handle, struct gssp_in_token *in_token) { - struct kvec *argv = &rqstp->rq_arg.head[0]; + struct xdr_stream *xdr = &rqstp->rq_arg_stream; unsigned int length, pgto_offs, pgfrom_offs; - size_t inlen, to_offs, from_offs; int pages, i, pgto, pgfrom; + size_t to_offs, from_offs; + u32 inlen; if (dup_netobj(in_handle, &gc->gc_ctx)) return SVC_CLOSE; - inlen = svc_getnl(argv); - if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) { - kfree(in_handle->data); - return SVC_DENIED; - } + /* + * RFC 2203 Section 5.2.2 + * + * struct rpc_gss_init_arg { + * opaque gss_token<>; + * }; + */ + if (xdr_stream_decode_u32(xdr, &inlen) < 0) + goto out_denied_free; + if (inlen > xdr_stream_remaining(xdr)) + goto out_denied_free; pages = DIV_ROUND_UP(inlen, PAGE_SIZE); in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); - if (!in_token->pages) { - kfree(in_handle->data); - return SVC_DENIED; - } + if (!in_token->pages) + goto out_denied_free; in_token->page_base = 0; in_token->page_len = inlen; for (i = 0; i < pages; i++) { in_token->pages[i] = alloc_page(GFP_KERNEL); if (!in_token->pages[i]) { - kfree(in_handle->data); gss_free_in_token_pages(in_token); - return SVC_DENIED; + goto out_denied_free; } } - length = min_t(unsigned int, inlen, argv->iov_len); - memcpy(page_address(in_token->pages[0]), argv->iov_base, length); + length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p); + memcpy(page_address(in_token->pages[0]), xdr->p, length); inlen -= length; to_offs = length; @@ -1167,6 +1171,10 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, inlen -= length; } return 0; + +out_denied_free: + kfree(in_handle->data); + return SVC_DENIED; } static inline int @@ -1196,27 +1204,45 @@ gss_write_resv(struct kvec *resv, size_t size_limit, * the upcall results are available, write the verifier and result. * Otherwise, drop the request pending an answer to the upcall. */ -static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, - struct rpc_gss_wire_cred *gc) +static int +svcauth_gss_legacy_init(struct svc_rqst *rqstp, + struct rpc_gss_wire_cred *gc) { - struct kvec *argv = &rqstp->rq_arg.head[0]; + struct xdr_stream *xdr = &rqstp->rq_arg_stream; struct kvec *resv = &rqstp->rq_res.head[0]; struct rsi *rsip, rsikey; - struct xdr_netobj tmpobj; + __be32 *p; + u32 len; int ret; struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); memset(&rsikey, 0, sizeof(rsikey)); if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) return SVC_CLOSE; - if (svc_safe_getnetobj(argv, &tmpobj)) { + + /* + * RFC 2203 Section 5.2.2 + * + * struct rpc_gss_init_arg { + * opaque gss_token<>; + * }; + */ + if (xdr_stream_decode_u32(xdr, &len) < 0) { + kfree(rsikey.in_handle.data); + return SVC_DENIED; + } + p = xdr_inline_decode(xdr, len); + if (!p) { kfree(rsikey.in_handle.data); return SVC_DENIED; } - if (dup_netobj(&rsikey.in_token, &tmpobj)) { + rsikey.in_token.data = kmalloc(len, GFP_KERNEL); + if (ZERO_OR_NULL_PTR(rsikey.in_token.data)) { kfree(rsikey.in_handle.data); return SVC_CLOSE; } + memcpy(rsikey.in_token.data, p, len); + rsikey.in_token.len = len; /* Perform upcall, or find upcall result: */ rsip = rsi_lookup(sn->rsi_cache, &rsikey); @@ -1237,7 +1263,6 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, rsip->major_status, rsip->minor_status)) goto out; - svcxdr_init_decode(rqstp); ret = SVC_COMPLETE; out: cache_put(&rsip->h, sn->rsi_cache); @@ -1366,7 +1391,6 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, ud.major_status, ud.minor_status)) goto out; - svcxdr_init_decode(rqstp); ret = SVC_COMPLETE; out: gss_free_in_token_pages(&ud.in_token); @@ -1404,14 +1428,19 @@ static bool use_gss_proxy(struct net *net) static noinline_for_stack int svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc) { - struct kvec *argv = rqstp->rq_arg.head; + struct xdr_stream *xdr = &rqstp->rq_arg_stream; + u32 flavor, len; + void *body; - if (argv->iov_len < 2 * 4) - return SVC_DENIED; - if (svc_getnl(argv) != RPC_AUTH_NULL) - return SVC_DENIED; - if (svc_getnl(argv) != 0) + svcxdr_init_decode(rqstp); + + /* Call's verf field: */ + if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0) + return SVC_GARBAGE; + if (flavor != RPC_AUTH_NULL || len != 0) { + rqstp->rq_auth_stat = rpc_autherr_badverf; return SVC_DENIED; + } if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) { rqstp->rq_auth_stat = rpc_autherr_badcred; |