From nobody Wed Aug 7 13:44:52 2024 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4WfBHn11qbz5S9LM; Wed, 07 Aug 2024 13:44:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4WfBHm5sjxz4jdF; Wed, 7 Aug 2024 13:44:52 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723038292; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6ssOh0VlVGLt2Znpe/kY4Z+7jOBABBe/mZnESWordA0=; b=BWWj0oU3n/Xz+U+gYLopcywH9XWjzOWioFCfd1x2rHGRCjogH+bafOV9KYVBEFWygCTMR3 PyK1atf6e0IFF0amwNapZO8UFPoaz3pSO/fv5Ah4Br95NWXKfnbVxnsP9Nyh1ZO4rn35pl qskjz8ZJrpqHw8JruwO/mxX02sBPZ1Wb12f/j1pFM/6IZ/hzNxkpzcs//uIbwNniKAmxCg d5uCa5G9+6VTrhDQUgs+vGY0VQKbbuvHy6pOORoqUNAMpn9G/bhwwrgezRWzyq6Px+NZmx CT8N8evAA11na4fTzn917QATAoQEPGwCSQyqM0jjZoNcWjte+PJcNYsyKOB2zA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1723038292; a=rsa-sha256; cv=none; b=LWqyNBFSQ87kAc3x9KWxPJTlVdd1eZdmkOViF3VXxeTvXzhF5p/kuRS7qZHoJpAvz44gmO QT80GmPL9v67HawiFLGe+mtW40E/wHOJ+Z+fu3ex/UkK3gRPoa4dUlo+5xi1xCqmfJBV9a FNJPwYas4L0LGRE4Wz7UCnqSSKhxW5Az3nprkCLAXeZYSJUJDz4pLJFbtvSIZl6hMb+TMl z2IW7GuK09HV1YO8uHI+U0SGW+FoqaV9fDJ5Rw3Ikl4mAvlDp4qOzgnbmPCn7vKh0/qh6j Zt6q+YG0rqGdFCy3pZ4UYlOetvm9Ukelgl46GhjJq4d3R/9+QGA20Ks4PnolqQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723038292; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6ssOh0VlVGLt2Znpe/kY4Z+7jOBABBe/mZnESWordA0=; b=ahz5q2w/0iF9z6VXe6G2zSlDk5/FDL9ZvSxGRYZ59lmH7l2AzwIxmzs1jpv+BuIAP4oCEp MhDqDvXpYE1pcGWDL6XtypVEbIXVdmhBHv5OCWtEKbVOeshduPeYUOtp8iu58OoSy0pgIj IYHrMDovHmraV/RKOACpGqlJodIGB1645KzF6zNASwUlJfCi6K7kFe5PSjQbTZMV2O5G2e Aa8eiv+zxa3sJ74g6Lg/G0D6x6djXh4JmJCQPr5UkUqPW/NAZKaPPLNxiSdeN+/AMOj/7f /16cR6Ly/9/GmHs13wf7Wn7YtbjgmENY07xDu2ooGJf79wOW96VzQjgnBMw1WA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4WfBHm5TsYzt3y; Wed, 7 Aug 2024 13:44:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 477Diq8X034647; Wed, 7 Aug 2024 13:44:52 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 477DiquT034644; Wed, 7 Aug 2024 13:44:52 GMT (envelope-from git) Date: Wed, 7 Aug 2024 13:44:52 GMT Message-Id: <202408071344.477DiquT034644@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 3d5cb2b9a97c - releng/13.3 - nfscl: Scan readdir reply filenames for invalid characters List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/releng/13.3 X-Git-Reftype: branch X-Git-Commit: 3d5cb2b9a97c54ae013568a358f24e8eca51c75f Auto-Submitted: auto-generated The branch releng/13.3 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=3d5cb2b9a97c54ae013568a358f24e8eca51c75f commit 3d5cb2b9a97c54ae013568a358f24e8eca51c75f Author: Rick Macklem AuthorDate: 2024-07-21 22:56:16 +0000 Commit: Mark Johnston CommitDate: 2024-08-07 13:37:00 +0000 nfscl: Scan readdir reply filenames for invalid characters The NFS RFCs are pretty loose with respect to what characters can be in a filename returned by a Readdir. However, FreeBSD, as a POSIX system will not handle imbedded '/' or nul characters in file names. Also, for NFSv4, the file names "." and ".." are handcrafted on the client and should not be returned by a NFSv4 server. This patch scans for the above in filenames returned by Readdir and ignores any entry returned by Readdir which has them in it. Because an imbedded nul would be a string terminator, it was not possible to code this check efficiently using string(3) functions. Approved by: so Security: FreeBSD-SA-24:07.nfsclient Security: CVE-2024-6759 Reported by: Apple Security Engineering and Architecture (SEAR) (cherry picked from commit 026cdaa3b3a92574d9ac3155216e5cc0b0bd4c51) (cherry picked from commit 0172b5145ad9435569978ed4d268b9f65ac59526) --- sys/fs/nfsclient/nfs_clrpcops.c | 137 ++++++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 27 deletions(-) diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 899d81efcf7c..c49f19fb0450 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -140,6 +140,7 @@ static int nfsrpc_createv4(vnode_t , char *, int, struct vattr *, nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *, int *, void *, int *); +static bool nfscl_invalidfname(bool, char *, int); static int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *, struct nfscllockowner *, u_int64_t, u_int64_t, u_int32_t, struct ucred *, NFSPROC_T *, int); @@ -2997,6 +2998,31 @@ nfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred, return (error); } +/* + * Check to make sure the file name in a Readdir reply is valid. + */ +static bool +nfscl_invalidfname(bool is_v4, char *name, int len) +{ + int i; + char *cp; + + if (is_v4 && ((len == 1 && name[0] == '.') || + (len == 2 && name[0] == '.' && name[1] == '.'))) { + printf("Readdir NFSv4 reply has dot or dotdot in it\n"); + return (true); + } + cp = name; + for (i = 0; i < len; i++, cp++) { + if (*cp == '/' || *cp == '\0') { + printf("Readdir reply file name had imbedded / or nul" + " byte\n"); + return (true); + } + } + return (false); +} + /* * Readdir rpc. * Always returns with either uio_resid unchanged, if you are at the @@ -3049,6 +3075,8 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, KASSERT(uiop->uio_iovcnt == 1 && (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0, ("nfs readdirrpc bad uio")); + KASSERT(uiop->uio_segflg == UIO_SYSSPACE, + ("nfsrpc_readdir: uio userspace")); ncookie.lval[0] = ncookie.lval[1] = 0; /* * There is no point in reading a lot more than uio_resid, however @@ -3307,6 +3335,17 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, uiop->uio_resid) bigenough = 0; if (bigenough) { + struct iovec saviov; + off_t savoff; + ssize_t savresid; + int savblksiz; + + saviov.iov_base = uiop->uio_iov->iov_base; + saviov.iov_len = uiop->uio_iov->iov_len; + savoff = uiop->uio_offset; + savresid = uiop->uio_resid; + savblksiz = blksiz; + dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_pad0 = dp->d_pad1 = 0; dp->d_off = 0; @@ -3322,20 +3361,35 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; + cp = uiop->uio_iov->iov_base; error = nfsm_mbufuio(nd, uiop, len); if (error) goto nfsmout; - cp = uiop->uio_iov->iov_base; - tlen -= len; - NFSBZERO(cp, tlen); - cp += tlen; /* points to cookie storage */ - tl2 = (u_int32_t *)cp; - uiop->uio_iov->iov_base = - (char *)uiop->uio_iov->iov_base + tlen + - NFSX_HYPER; - uiop->uio_iov->iov_len -= tlen + NFSX_HYPER; - uiop->uio_resid -= tlen + NFSX_HYPER; - uiop->uio_offset += (tlen + NFSX_HYPER); + /* Check for an invalid file name. */ + if (nfscl_invalidfname( + (nd->nd_flag & ND_NFSV4) != 0, cp, len)) { + /* Skip over this entry. */ + uiop->uio_iov->iov_base = + saviov.iov_base; + uiop->uio_iov->iov_len = + saviov.iov_len; + uiop->uio_offset = savoff; + uiop->uio_resid = savresid; + blksiz = savblksiz; + } else { + cp = uiop->uio_iov->iov_base; + tlen -= len; + NFSBZERO(cp, tlen); + cp += tlen; /* points to cookie store */ + tl2 = (u_int32_t *)cp; + uiop->uio_iov->iov_base = + (char *)uiop->uio_iov->iov_base + + tlen + NFSX_HYPER; + uiop->uio_iov->iov_len -= tlen + + NFSX_HYPER; + uiop->uio_resid -= tlen + NFSX_HYPER; + uiop->uio_offset += (tlen + NFSX_HYPER); + } } else { error = nfsm_advance(nd, NFSM_RNDUP(len), -1); if (error) @@ -3503,6 +3557,8 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, KASSERT(uiop->uio_iovcnt == 1 && (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0, ("nfs readdirplusrpc bad uio")); + KASSERT(uiop->uio_segflg == UIO_SYSSPACE, + ("nfsrpc_readdirplus: uio userspace")); ncookie.lval[0] = ncookie.lval[1] = 0; timespecclear(&dctime); *attrflagp = 0; @@ -3738,6 +3794,17 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, uiop->uio_resid) bigenough = 0; if (bigenough) { + struct iovec saviov; + off_t savoff; + ssize_t savresid; + int savblksiz; + + saviov.iov_base = uiop->uio_iov->iov_base; + saviov.iov_len = uiop->uio_iov->iov_len; + savoff = uiop->uio_offset; + savresid = uiop->uio_resid; + savblksiz = blksiz; + dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_pad0 = dp->d_pad1 = 0; dp->d_off = 0; @@ -3756,25 +3823,41 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, cnp->cn_nameptr = uiop->uio_iov->iov_base; cnp->cn_namelen = len; NFSCNHASHZERO(cnp); + cp = uiop->uio_iov->iov_base; error = nfsm_mbufuio(nd, uiop, len); if (error) goto nfsmout; - cp = uiop->uio_iov->iov_base; - tlen -= len; - NFSBZERO(cp, tlen); - cp += tlen; /* points to cookie storage */ - tl2 = (u_int32_t *)cp; - if (len == 2 && cnp->cn_nameptr[0] == '.' && - cnp->cn_nameptr[1] == '.') - isdotdot = 1; - else - isdotdot = 0; - uiop->uio_iov->iov_base = - (char *)uiop->uio_iov->iov_base + tlen + - NFSX_HYPER; - uiop->uio_iov->iov_len -= tlen + NFSX_HYPER; - uiop->uio_resid -= tlen + NFSX_HYPER; - uiop->uio_offset += (tlen + NFSX_HYPER); + /* Check for an invalid file name. */ + if (nfscl_invalidfname( + (nd->nd_flag & ND_NFSV4) != 0, cp, len)) { + /* Skip over this entry. */ + uiop->uio_iov->iov_base = + saviov.iov_base; + uiop->uio_iov->iov_len = + saviov.iov_len; + uiop->uio_offset = savoff; + uiop->uio_resid = savresid; + blksiz = savblksiz; + } else { + cp = uiop->uio_iov->iov_base; + tlen -= len; + NFSBZERO(cp, tlen); + cp += tlen; /* points to cookie store */ + tl2 = (u_int32_t *)cp; + if (len == 2 && + cnp->cn_nameptr[0] == '.' && + cnp->cn_nameptr[1] == '.') + isdotdot = 1; + else + isdotdot = 0; + uiop->uio_iov->iov_base = + (char *)uiop->uio_iov->iov_base + + tlen + NFSX_HYPER; + uiop->uio_iov->iov_len -= tlen + + NFSX_HYPER; + uiop->uio_resid -= tlen + NFSX_HYPER; + uiop->uio_offset += (tlen + NFSX_HYPER); + } } else { error = nfsm_advance(nd, NFSM_RNDUP(len), -1); if (error)