Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

SUNRPC: Convert svcauth_unix_accept() to use xdr_stream

Done as part of hardening the server-side RPC header decoding path.

Since the server-side of the Linux kernel SunRPC implementation
ignores the contents of the Call's machinename field, there's no
need for its RPC_AUTH_UNIX authenticator to reject names that are
larger than UNX_MAXNODENAME.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

+56 -20
+5
include/linux/sunrpc/msg_prot.h
··· 34 34 RPC_AUTH_GSS_SPKMP = 390011, 35 35 }; 36 36 37 + /* Maximum size (in octets) of the machinename in an AUTH_UNIX 38 + * credential (per RFC 5531 Appendix A) 39 + */ 40 + #define RPC_MAX_MACHINENAME (255) 41 + 37 42 /* Maximum size (in bytes) of an rpc credential or verifier */ 38 43 #define RPC_MAX_AUTH_SIZE (400) 39 44
+51 -20
net/sunrpc/svcauth_unix.c
··· 867 867 }; 868 868 869 869 870 + /** 871 + * svcauth_unix_accept - Decode and validate incoming RPC_AUTH_SYS credential 872 + * @rqstp: RPC transaction 873 + * 874 + * Return values: 875 + * %SVC_OK: Both credential and verifier are valid 876 + * %SVC_DENIED: Credential or verifier is not valid 877 + * %SVC_GARBAGE: Failed to decode credential or verifier 878 + * %SVC_CLOSE: Temporary failure 879 + * 880 + * rqstp->rq_auth_stat is set as mandated by RFC 5531. 881 + */ 870 882 static int 871 883 svcauth_unix_accept(struct svc_rqst *rqstp) 872 884 { 873 - struct kvec *argv = &rqstp->rq_arg.head[0]; 874 885 struct kvec *resv = &rqstp->rq_res.head[0]; 886 + struct xdr_stream *xdr = &rqstp->rq_arg_stream; 875 887 struct svc_cred *cred = &rqstp->rq_cred; 876 888 struct user_namespace *userns; 877 - u32 slen, i; 878 - int len = argv->iov_len; 889 + u32 flavor, len, i; 890 + void *body; 891 + __be32 *p; 879 892 880 - if ((len -= 3*4) < 0) 893 + svcxdr_init_decode(rqstp); 894 + 895 + /* 896 + * This implementation ignores the length of the Call's 897 + * credential body field and the timestamp and machinename 898 + * fields. 899 + */ 900 + p = xdr_inline_decode(xdr, XDR_UNIT * 3); 901 + if (!p) 902 + return SVC_GARBAGE; 903 + len = be32_to_cpup(p + 2); 904 + if (len > RPC_MAX_MACHINENAME) 905 + return SVC_GARBAGE; 906 + if (!xdr_inline_decode(xdr, len)) 881 907 return SVC_GARBAGE; 882 908 883 - svc_getu32(argv); /* length */ 884 - svc_getu32(argv); /* time stamp */ 885 - slen = XDR_QUADLEN(svc_getnl(argv)); /* machname length */ 886 - if (slen > 64 || (len -= (slen + 3)*4) < 0) 887 - goto badcred; 888 - argv->iov_base = (void*)((__be32*)argv->iov_base + slen); /* skip machname */ 889 - argv->iov_len -= slen*4; 890 909 /* 891 910 * Note: we skip uid_valid()/gid_valid() checks here for 892 911 * backwards compatibility with clients that use -1 id's. ··· 915 896 */ 916 897 userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ? 917 898 rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns; 918 - cred->cr_uid = make_kuid(userns, svc_getnl(argv)); /* uid */ 919 - cred->cr_gid = make_kgid(userns, svc_getnl(argv)); /* gid */ 920 - slen = svc_getnl(argv); /* gids length */ 921 - if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0) 899 + if (xdr_stream_decode_u32(xdr, &i) < 0) 900 + return SVC_GARBAGE; 901 + cred->cr_uid = make_kuid(userns, i); 902 + if (xdr_stream_decode_u32(xdr, &i) < 0) 903 + return SVC_GARBAGE; 904 + cred->cr_gid = make_kgid(userns, i); 905 + 906 + if (xdr_stream_decode_u32(xdr, &len) < 0) 907 + return SVC_GARBAGE; 908 + if (len > UNX_NGROUPS) 922 909 goto badcred; 923 - cred->cr_group_info = groups_alloc(slen); 910 + p = xdr_inline_decode(xdr, XDR_UNIT * len); 911 + if (!p) 912 + return SVC_GARBAGE; 913 + cred->cr_group_info = groups_alloc(len); 924 914 if (cred->cr_group_info == NULL) 925 915 return SVC_CLOSE; 926 - for (i = 0; i < slen; i++) { 927 - kgid_t kgid = make_kgid(userns, svc_getnl(argv)); 916 + for (i = 0; i < len; i++) { 917 + kgid_t kgid = make_kgid(userns, be32_to_cpup(p++)); 928 918 cred->cr_group_info->gid[i] = kgid; 929 919 } 930 920 groups_sort(cred->cr_group_info); 931 - if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { 921 + 922 + /* Call's verf field: */ 923 + if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0) 924 + return SVC_GARBAGE; 925 + if (flavor != RPC_AUTH_NULL || len != 0) { 932 926 rqstp->rq_auth_stat = rpc_autherr_badverf; 933 927 return SVC_DENIED; 934 928 } ··· 951 919 svc_putnl(resv, 0); 952 920 953 921 rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX; 954 - svcxdr_init_decode(rqstp); 955 922 return SVC_OK; 956 923 957 924 badcred: