mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
unix: Preserve FD_RESOLVE_BENEATH when passing an fd
The FD_RESOLVE_BENEATH flag is supposed to be sticky. It's set when you receive an fd from a different jail and preserved by openat(<dfd>) etc.. However, if you send the fd to yourself, the flag is stripped since SCM_RIGHTS message don't preserve file descriptor flags. Fix this by preserving those flags and checking for UF_RESOLVE_BENEATH in restrict_rights(). Approved by: so Security: FreeBSD-SA-26:66.jail Security: CVE-2026-101306 Fixes:350ba9672a("unix: Set O_RESOLVE_BENEATH on fds transferred between jails") Reviewed by: kib MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D58317 (cherry picked from commit586e2b3d89) (cherry picked from commit441885dcff)
This commit is contained in:
1 parent
1c1575015c
commit
9d0644e7a4
2 files changed
+50
-8
No files matched your search
+19
-8
@@ -2433,15 +2433,25 @@ unp_freerights(struct filedescent **fdep, int fdcount)
|
||||
free(fdep[0], M_FILECAPS);
|
||||
}
|
||||
|
||||
static bool
|
||||
restrict_rights(struct file *fp, struct thread *td)
|
||||
/*
|
||||
* Flags to set on the receiving side when externalizing a file descriptor.
|
||||
* When transferring fds between jails, ensure that the receiver cannot use
|
||||
* a dirfd to escape the jail chroot.
|
||||
*/
|
||||
static int
|
||||
externalize_fdflags(struct filedescent *fde, struct thread *td)
|
||||
{
|
||||
struct prison *prison1, *prison2;
|
||||
|
||||
prison1 = fp->f_cred->cr_prison;
|
||||
if ((fde->fde_flags & UF_RESOLVE_BENEATH) != 0)
|
||||
return (O_RESOLVE_BENEATH);
|
||||
prison1 = fde->fde_file->f_cred->cr_prison;
|
||||
prison2 = td->td_ucred->cr_prison;
|
||||
return (prison1 != prison2 && prison1->pr_root != prison2->pr_root &&
|
||||
prison2 != &prison0);
|
||||
if (prison1 != prison2 && prison1->pr_root != prison2->pr_root &&
|
||||
prison2 != &prison0)
|
||||
return (O_RESOLVE_BENEATH);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -2506,9 +2516,9 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
|
||||
struct file *fp;
|
||||
|
||||
fp = fdep[i]->fde_file;
|
||||
_finstall(fdesc, fp, *fdp, fdflags |
|
||||
(restrict_rights(fp, td) ?
|
||||
O_RESOLVE_BENEATH : 0), &fdep[i]->fde_caps);
|
||||
_finstall(fdesc, fp, *fdp,
|
||||
fdflags | externalize_fdflags(fdep[i], td),
|
||||
&fdep[i]->fde_caps);
|
||||
unp_externalize_fp(fp);
|
||||
}
|
||||
|
||||
@@ -2744,6 +2754,7 @@ unp_internalize(struct mbuf **controlp, struct thread *td,
|
||||
fdep[i]->fde_file = fde->fde_file;
|
||||
filecaps_copy(&fde->fde_caps,
|
||||
&fdep[i]->fde_caps, true);
|
||||
fdep[i]->fde_flags = fde->fde_flags;
|
||||
unp_internalize_fp(fdep[i]->fde_file);
|
||||
}
|
||||
FILEDESC_SUNLOCK(fdesc);
|
||||
|
||||
@@ -1076,6 +1076,36 @@ ATF_TC_CLEANUP(cross_jail_dirfd, tc)
|
||||
err(1, "jail_remove");
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that FD_RESOLVE_BENEATH is preserved when an fd is passed over a UNIX
|
||||
* domain socket.
|
||||
*/
|
||||
ATF_TC_WITHOUT_HEAD(resolve_beneath_preserved);
|
||||
ATF_TC_BODY(resolve_beneath_preserved, tc)
|
||||
{
|
||||
int fd[2], getfd, putfd, fdflags;
|
||||
|
||||
domainsocketpair(fd);
|
||||
tempfile(&putfd);
|
||||
|
||||
fdflags = fcntl(putfd, F_GETFD);
|
||||
ATF_REQUIRE(fdflags != -1);
|
||||
ATF_REQUIRE(fcntl(putfd, F_SETFD, fdflags | FD_RESOLVE_BENEATH) != -1);
|
||||
ATF_REQUIRE((fcntl(putfd, F_GETFD) & FD_RESOLVE_BENEATH) != 0);
|
||||
|
||||
sendfd(fd[0], putfd);
|
||||
recvfd(fd[1], &getfd, 0);
|
||||
|
||||
fdflags = fcntl(getfd, F_GETFD);
|
||||
ATF_REQUIRE(fdflags != -1);
|
||||
ATF_REQUIRE_MSG((fdflags & FD_RESOLVE_BENEATH) != 0,
|
||||
"FD_RESOLVE_BENEATH was not preserved across SCM_RIGHTS transfer");
|
||||
|
||||
ATF_REQUIRE(close(putfd) == 0);
|
||||
ATF_REQUIRE(close(getfd) == 0);
|
||||
closesocketpair(fd);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
|
||||
@@ -1095,6 +1125,7 @@ ATF_TP_ADD_TCS(tp)
|
||||
ATF_TP_ADD_TC(tp, copyout_rights_error);
|
||||
ATF_TP_ADD_TC(tp, empty_rights_message);
|
||||
ATF_TP_ADD_TC(tp, cross_jail_dirfd);
|
||||
ATF_TP_ADD_TC(tp, resolve_beneath_preserved);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
Reference in new issue
Block a user