SUNRPC: Convert gss_verify_header() to use xdr_stream

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>
This commit is contained in:
Chuck Lever 2023-01-02 12:07:13 -05:00
Родитель 42140718ea
Коммит 0653028e8f
1 изменённых файлов: 29 добавлений и 23 удалений

Просмотреть файл

@ -732,38 +732,48 @@ svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
}
/*
* Verify the checksum on the header and return SVC_OK on success.
* Otherwise, return SVC_DROP (in the case of a bad sequence number)
* or return SVC_DENIED and indicate error in rqstp->rq_auth_stat.
* Decode and verify a Call's verifier field. For RPC_AUTH_GSS Calls,
* the body of this field contains a variable length checksum.
*
* GSS-specific auth_stat values are mandated by RFC 2203 Section
* 5.3.3.3.
*/
static int
gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
__be32 *rpcstart, struct rpc_gss_wire_cred *gc)
svcauth_gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
__be32 *rpcstart, struct rpc_gss_wire_cred *gc)
{
struct xdr_stream *xdr = &rqstp->rq_arg_stream;
struct gss_ctx *ctx_id = rsci->mechctx;
u32 flavor, maj_stat;
struct xdr_buf rpchdr;
struct xdr_netobj checksum;
u32 flavor = 0;
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec iov;
/* data to compute the checksum over: */
/*
* Compute the checksum of the incoming Call from the
* XID field to credential field:
*/
iov.iov_base = rpcstart;
iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
iov.iov_len = (u8 *)xdr->p - (u8 *)rpcstart;
xdr_buf_from_iov(&iov, &rpchdr);
rqstp->rq_auth_stat = rpc_autherr_badverf;
if (argv->iov_len < 4)
/* Call's verf field: */
if (xdr_stream_decode_opaque_auth(xdr, &flavor,
(void **)&checksum.data,
&checksum.len) < 0) {
rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
flavor = svc_getnl(argv);
if (flavor != RPC_AUTH_GSS)
return SVC_DENIED;
if (svc_safe_getnetobj(argv, &checksum))
}
if (flavor != RPC_AUTH_GSS) {
rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
}
if (rqstp->rq_deferred) /* skip verification of revisited request */
if (rqstp->rq_deferred)
return SVC_OK;
if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
maj_stat = gss_verify_mic(ctx_id, &rpchdr, &checksum);
if (maj_stat != GSS_S_COMPLETE) {
trace_rpcgss_svc_mic(rqstp, maj_stat);
rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
return SVC_DENIED;
}
@ -1431,8 +1441,6 @@ svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
u32 flavor, len;
void *body;
svcxdr_init_decode(rqstp);
/* Call's verf field: */
if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
return SVC_GARBAGE;
@ -1603,6 +1611,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
goto auth_err;
svcxdr_init_decode(rqstp);
rqstp->rq_auth_stat = rpc_autherr_badverf;
switch (gc->gc_proc) {
case RPC_GSS_PROC_INIT:
@ -1615,7 +1624,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
if (!rsci)
goto auth_err;
switch (gss_verify_header(rqstp, rsci, rpcstart, gc)) {
switch (svcauth_gss_verify_header(rqstp, rsci, rpcstart, gc)) {
case SVC_OK:
break;
case SVC_DENIED:
@ -1650,13 +1659,11 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
rqstp->rq_auth_stat = rpc_autherr_badcred;
switch (gc->gc_svc) {
case RPC_GSS_SVC_NONE:
svcxdr_init_decode(rqstp);
break;
case RPC_GSS_SVC_INTEGRITY:
/* placeholders for length and seq. number: */
svc_putnl(resv, 0);
svc_putnl(resv, 0);
svcxdr_init_decode(rqstp);
if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq,
rsci->mechctx))
goto garbage_args;
@ -1666,7 +1673,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
/* placeholders for length and seq. number: */
svc_putnl(resv, 0);
svc_putnl(resv, 0);
svcxdr_init_decode(rqstp);
if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq,
rsci->mechctx))
goto garbage_args;