Commit c020fa69 authored by Chuck Lever's avatar Chuck Lever

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: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 1cbfb921
...@@ -1112,39 +1112,43 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, ...@@ -1112,39 +1112,43 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
struct xdr_netobj *in_handle, struct xdr_netobj *in_handle,
struct gssp_in_token *in_token) 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; unsigned int length, pgto_offs, pgfrom_offs;
size_t inlen, to_offs, from_offs;
int pages, i, pgto, pgfrom; int pages, i, pgto, pgfrom;
size_t to_offs, from_offs;
u32 inlen;
if (dup_netobj(in_handle, &gc->gc_ctx)) if (dup_netobj(in_handle, &gc->gc_ctx))
return SVC_CLOSE; return SVC_CLOSE;
inlen = svc_getnl(argv); /*
if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) { * RFC 2203 Section 5.2.2
kfree(in_handle->data); *
return SVC_DENIED; * 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); pages = DIV_ROUND_UP(inlen, PAGE_SIZE);
in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL);
if (!in_token->pages) { if (!in_token->pages)
kfree(in_handle->data); goto out_denied_free;
return SVC_DENIED;
}
in_token->page_base = 0; in_token->page_base = 0;
in_token->page_len = inlen; in_token->page_len = inlen;
for (i = 0; i < pages; i++) { for (i = 0; i < pages; i++) {
in_token->pages[i] = alloc_page(GFP_KERNEL); in_token->pages[i] = alloc_page(GFP_KERNEL);
if (!in_token->pages[i]) { if (!in_token->pages[i]) {
kfree(in_handle->data);
gss_free_in_token_pages(in_token); gss_free_in_token_pages(in_token);
return SVC_DENIED; goto out_denied_free;
} }
} }
length = min_t(unsigned int, inlen, argv->iov_len); length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p);
memcpy(page_address(in_token->pages[0]), argv->iov_base, length); memcpy(page_address(in_token->pages[0]), xdr->p, length);
inlen -= length; inlen -= length;
to_offs = length; to_offs = length;
...@@ -1167,6 +1171,10 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, ...@@ -1167,6 +1171,10 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
inlen -= length; inlen -= length;
} }
return 0; return 0;
out_denied_free:
kfree(in_handle->data);
return SVC_DENIED;
} }
static inline int static inline int
...@@ -1196,27 +1204,45 @@ gss_write_resv(struct kvec *resv, size_t size_limit, ...@@ -1196,27 +1204,45 @@ gss_write_resv(struct kvec *resv, size_t size_limit,
* the upcall results are available, write the verifier and result. * the upcall results are available, write the verifier and result.
* Otherwise, drop the request pending an answer to the upcall. * Otherwise, drop the request pending an answer to the upcall.
*/ */
static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, static int
struct rpc_gss_wire_cred *gc) 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 kvec *resv = &rqstp->rq_res.head[0];
struct rsi *rsip, rsikey; struct rsi *rsip, rsikey;
struct xdr_netobj tmpobj; __be32 *p;
u32 len;
int ret; int ret;
struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
memset(&rsikey, 0, sizeof(rsikey)); memset(&rsikey, 0, sizeof(rsikey));
if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
return SVC_CLOSE; 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); kfree(rsikey.in_handle.data);
return SVC_DENIED; 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); kfree(rsikey.in_handle.data);
return SVC_CLOSE; return SVC_CLOSE;
} }
memcpy(rsikey.in_token.data, p, len);
rsikey.in_token.len = len;
/* Perform upcall, or find upcall result: */ /* Perform upcall, or find upcall result: */
rsip = rsi_lookup(sn->rsi_cache, &rsikey); rsip = rsi_lookup(sn->rsi_cache, &rsikey);
...@@ -1237,7 +1263,6 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, ...@@ -1237,7 +1263,6 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
rsip->major_status, rsip->minor_status)) rsip->major_status, rsip->minor_status))
goto out; goto out;
svcxdr_init_decode(rqstp);
ret = SVC_COMPLETE; ret = SVC_COMPLETE;
out: out:
cache_put(&rsip->h, sn->rsi_cache); cache_put(&rsip->h, sn->rsi_cache);
...@@ -1366,7 +1391,6 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, ...@@ -1366,7 +1391,6 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
ud.major_status, ud.minor_status)) ud.major_status, ud.minor_status))
goto out; goto out;
svcxdr_init_decode(rqstp);
ret = SVC_COMPLETE; ret = SVC_COMPLETE;
out: out:
gss_free_in_token_pages(&ud.in_token); gss_free_in_token_pages(&ud.in_token);
...@@ -1404,14 +1428,19 @@ static bool use_gss_proxy(struct net *net) ...@@ -1404,14 +1428,19 @@ static bool use_gss_proxy(struct net *net)
static noinline_for_stack int static noinline_for_stack int
svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc) 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) svcxdr_init_decode(rqstp);
return SVC_DENIED;
if (svc_getnl(argv) != RPC_AUTH_NULL) /* Call's verf field: */
return SVC_DENIED; if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
if (svc_getnl(argv) != 0) return SVC_GARBAGE;
if (flavor != RPC_AUTH_NULL || len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED; return SVC_DENIED;
}
if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) { if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badcred; rqstp->rq_auth_stat = rpc_autherr_badcred;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment