git: b165e9e3ea4e - main - Add fchroot(2)
- Reply: Konstantin Belousov : "Re: git: b165e9e3ea4e - main - Add fchroot(2)"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 29 Nov 2024 12:22:16 UTC
The branch main has been updated by trasz:
URL: https://cgit.FreeBSD.org/src/commit/?id=b165e9e3ea4e327fc421d81c2a89242bd8720780
commit b165e9e3ea4e327fc421d81c2a89242bd8720780
Author: Edward Tomasz Napierala <trasz@FreeBSD.org>
AuthorDate: 2024-11-29 07:46:07 +0000
Commit: Edward Tomasz Napierala <trasz@FreeBSD.org>
CommitDate: 2024-11-29 12:10:02 +0000
Add fchroot(2)
This is similar to chroot(2), but takes a file descriptor instead
of path. Same syscall exists in NetBSD and Solaris. It is part of a larger
patch to make absolute pathnames usable in Capsicum mode, but should
be useful in other contexts too.
Reviewed By: brooks
Sponsored by: Innovate UK
Differential Revision: https://reviews.freebsd.org/D41564
---
include/unistd.h | 1 +
lib/libsys/Makefile.sys | 1 +
lib/libsys/Symbol.sys.map | 1 +
lib/libsys/chroot.2 | 40 ++++++++++++++++++-
share/man/man4/rights.4 | 5 ++-
sys/kern/subr_capability.c | 2 +
sys/kern/syscalls.master | 5 +++
sys/kern/vfs_syscalls.c | 84 +++++++++++++++++++++++++++++----------
sys/sys/caprights.h | 1 +
sys/sys/capsicum.h | 7 ++--
usr.bin/procstat/procstat_files.c | 1 +
11 files changed, 120 insertions(+), 28 deletions(-)
diff --git a/include/unistd.h b/include/unistd.h
index 48155bb2971b..8574b2ba9915 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -507,6 +507,7 @@ int exect(const char *, char * const *, char * const *);
int execvP(const char *, const char *, char * const *);
int execvpe(const char *, char * const *, char * const *);
int feature_present(const char *);
+int fchroot(int);
char *fflagstostr(u_long);
int getdomainname(char *, int);
int getentropy(void *, size_t);
diff --git a/lib/libsys/Makefile.sys b/lib/libsys/Makefile.sys
index 4be64a98bb96..04e767d50f86 100644
--- a/lib/libsys/Makefile.sys
+++ b/lib/libsys/Makefile.sys
@@ -399,6 +399,7 @@ MLINKS+=chmod.2 fchmod.2 \
MLINKS+=chown.2 fchown.2 \
chown.2 fchownat.2 \
chown.2 lchown.2
+MLINKS+=chroot.2 fchroot.2
MLINKS+=clock_gettime.2 clock_getres.2 \
clock_gettime.2 clock_settime.2
MLINKS+=closefrom.2 close_range.2
diff --git a/lib/libsys/Symbol.sys.map b/lib/libsys/Symbol.sys.map
index 85373b1f9cda..3e2f14497b07 100644
--- a/lib/libsys/Symbol.sys.map
+++ b/lib/libsys/Symbol.sys.map
@@ -378,6 +378,7 @@ FBSD_1.7 {
};
FBSD_1.8 {
+ fchroot;
getrlimitusage;
kcmp;
};
diff --git a/lib/libsys/chroot.2 b/lib/libsys/chroot.2
index af187bf30b2c..4c06e3673e03 100644
--- a/lib/libsys/chroot.2
+++ b/lib/libsys/chroot.2
@@ -25,11 +25,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd September 29, 2020
+.Dd July 15, 2024
.Dt CHROOT 2
.Os
.Sh NAME
-.Nm chroot
+.Nm chroot ,
+.Nm fchroot
.Nd change root directory
.Sh LIBRARY
.Lb libc
@@ -37,6 +38,8 @@
.In unistd.h
.Ft int
.Fn chroot "const char *dirname"
+.Ft int
+.Fn fchroot "int fd"
.Sh DESCRIPTION
The
.Fa dirname
@@ -92,6 +95,12 @@ will bypass the check for open directories,
mimicking the historic insecure behavior of
.Fn chroot
still present on other systems.
+.Pp
+The
+.Fn fchroot
+system call is identical to
+.Fn chroot
+except it takes a file descriptor instead of path.
.Sh RETURN VALUES
.Rv -std
.Sh ERRORS
@@ -124,6 +133,29 @@ An I/O error occurred while reading from or writing to the file system.
.It Bq Er EINTEGRITY
Corrupted data was detected while reading from the file system.
.El
+.Pp
+The
+.Fn fchroot
+system call
+will fail and the root directory will be unchanged if:
+.Bl -tag -width Er
+.It Bq Er EACCES
+Search permission is denied for the directory referenced by the
+file descriptor.
+.It Bq Er EBADF
+The argument
+.Fa fd
+is not a valid file descriptor.
+.It Bq Er EIO
+An I/O error occurred while reading from or writing to the file system.
+.It Bq Er EINTEGRITY
+Corrupted data was detected while reading from the file system.
+.It Bq Er ENOTDIR
+The file descriptor does not reference a directory.
+.It Bq Er EPERM
+The effective user ID is not the super-user, or one or more
+filedescriptors are open directories.
+.El
.Sh SEE ALSO
.Xr chdir 2 ,
.Xr jail 2
@@ -137,6 +169,10 @@ It was marked as
in
.St -susv2 ,
and was removed in subsequent standards.
+The
+.Fn fchroot
+system call first appeared in
+.Fx 15.0 .
.Sh BUGS
If the process is able to change its working directory to the target
directory, but another access control check fails (such as a check for
diff --git a/share/man/man4/rights.4 b/share/man/man4/rights.4
index 3e5e18fc65d8..0c24f6b45f88 100644
--- a/share/man/man4/rights.4
+++ b/share/man/man4/rights.4
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 27, 2024
+.Dd May 1, 2024
.Dt RIGHTS 4
.Os
.Sh NAME
@@ -209,6 +209,9 @@ An alias to
.Dv CAP_FCHOWN
and
.Dv CAP_LOOKUP .
+.It Dv CAP_FCHROOT
+Permit
+.Xr fchroot 2 .
.It Dv CAP_FCNTL
Permit
.Xr fcntl 2 .
diff --git a/sys/kern/subr_capability.c b/sys/kern/subr_capability.c
index 1f3a181a91cb..a97c16d6d7df 100644
--- a/sys/kern/subr_capability.c
+++ b/sys/kern/subr_capability.c
@@ -59,6 +59,7 @@ __read_mostly cap_rights_t cap_fchdir_rights;
__read_mostly cap_rights_t cap_fchflags_rights;
__read_mostly cap_rights_t cap_fchmod_rights;
__read_mostly cap_rights_t cap_fchown_rights;
+__read_mostly cap_rights_t cap_fchroot_rights;
__read_mostly cap_rights_t cap_fcntl_rights;
__read_mostly cap_rights_t cap_fexecve_rights;
__read_mostly cap_rights_t cap_flock_rights;
@@ -108,6 +109,7 @@ cap_rights_sysinit(void *arg)
cap_rights_init_one(&cap_fchflags_rights, CAP_FCHFLAGS);
cap_rights_init_one(&cap_fchmod_rights, CAP_FCHMOD);
cap_rights_init_one(&cap_fchown_rights, CAP_FCHOWN);
+ cap_rights_init_one(&cap_fchroot_rights, CAP_FCHROOT);
cap_rights_init_one(&cap_fcntl_rights, CAP_FCNTL);
cap_rights_init_one(&cap_fexecve_rights, CAP_FEXECVE);
cap_rights_init_one(&cap_flock_rights, CAP_FLOCK);
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index 2bbd20b5a5b0..e7f577d48426 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3341,5 +3341,10 @@
_Out_ rlim_t *res
);
}
+590 AUE_NULL STD {
+ int fchroot(
+ int fd
+ );
+ }
; vim: syntax=off
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index ab0e562e73aa..7a1677c945e3 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -967,18 +967,13 @@ static int unprivileged_chroot = 0;
SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_chroot, CTLFLAG_RW,
&unprivileged_chroot, 0,
"Unprivileged processes can use chroot(2)");
+
/*
- * Change notion of root (``/'') directory.
+ * Takes locked vnode, unlocks it before returning.
*/
-#ifndef _SYS_SYSPROTO_H_
-struct chroot_args {
- char *path;
-};
-#endif
-int
-sys_chroot(struct thread *td, struct chroot_args *uap)
+static int
+kern_chroot(struct thread *td, struct vnode *vp)
{
- struct nameidata nd;
struct proc *p;
int error;
@@ -989,30 +984,75 @@ sys_chroot(struct thread *td, struct chroot_args *uap)
if (unprivileged_chroot == 0 ||
(p->p_flag2 & P2_NO_NEW_PRIVS) == 0) {
PROC_UNLOCK(p);
- return (error);
+ goto e_vunlock;
}
PROC_UNLOCK(p);
}
- NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
- UIO_USERSPACE, uap->path);
- error = namei(&nd);
- if (error != 0)
- return (error);
- NDFREE_PNBUF(&nd);
- error = change_dir(nd.ni_vp, td);
+
+ error = change_dir(vp, td);
if (error != 0)
goto e_vunlock;
#ifdef MAC
- error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp);
+ error = mac_vnode_check_chroot(td->td_ucred, vp);
if (error != 0)
goto e_vunlock;
#endif
- VOP_UNLOCK(nd.ni_vp);
- error = pwd_chroot(td, nd.ni_vp);
- vrele(nd.ni_vp);
+ VOP_UNLOCK(vp);
+ error = pwd_chroot(td, vp);
+ vrele(vp);
return (error);
e_vunlock:
- vput(nd.ni_vp);
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Change notion of root (``/'') directory.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct chroot_args {
+ char *path;
+};
+#endif
+int
+sys_chroot(struct thread *td, struct chroot_args *uap)
+{
+ struct nameidata nd;
+ int error;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
+ UIO_USERSPACE, uap->path);
+ error = namei(&nd);
+ if (error != 0)
+ return (error);
+ NDFREE_PNBUF(&nd);
+ error = kern_chroot(td, nd.ni_vp);
+ return (error);
+}
+
+/*
+ * Change notion of root directory to a given file descriptor.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct fchroot_args {
+ int fd;
+};
+#endif
+int
+sys_fchroot(struct thread *td, struct fchroot_args *uap)
+{
+ struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ error = getvnode_path(td, uap->fd, &cap_fchroot_rights, &fp);
+ if (error != 0)
+ return (error);
+ vp = fp->f_vnode;
+ vrefact(vp);
+ fdrop(fp, td);
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ error = kern_chroot(td, vp);
return (error);
}
diff --git a/sys/sys/caprights.h b/sys/sys/caprights.h
index 32ae05172e24..62711545114d 100644
--- a/sys/sys/caprights.h
+++ b/sys/sys/caprights.h
@@ -66,6 +66,7 @@ extern cap_rights_t cap_fchdir_rights;
extern cap_rights_t cap_fchflags_rights;
extern cap_rights_t cap_fchmod_rights;
extern cap_rights_t cap_fchown_rights;
+extern cap_rights_t cap_fchroot_rights;
extern cap_rights_t cap_fcntl_rights;
extern cap_rights_t cap_fexecve_rights;
extern cap_rights_t cap_flock_rights;
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
index 5c6813d2a450..9b50986ede0a 100644
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -201,6 +201,9 @@
/* Allows for renameat(2) (target directory descriptor). */
#define CAP_RENAMEAT_TARGET (CAP_LOOKUP | 0x0000040000000000ULL)
+/* Allows for fchroot(2). */
+#define CAP_FCHROOT CAPRIGHT(0, 0x0000080000000000ULL)
+
#define CAP_SOCK_CLIENT \
(CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \
CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN)
@@ -210,11 +213,9 @@
CAP_SETSOCKOPT | CAP_SHUTDOWN)
/* All used bits for index 0. */
-#define CAP_ALL0 CAPRIGHT(0, 0x000007FFFFFFFFFFULL)
+#define CAP_ALL0 CAPRIGHT(0, 0x00000FFFFFFFFFFFULL)
/* Available bits for index 0. */
-#define CAP_UNUSED0_44 CAPRIGHT(0, 0x0000080000000000ULL)
-/* ... */
#define CAP_UNUSED0_57 CAPRIGHT(0, 0x0100000000000000ULL)
/* INDEX 1 */
diff --git a/usr.bin/procstat/procstat_files.c b/usr.bin/procstat/procstat_files.c
index 359c2d13c86d..bd9bbfc358c9 100644
--- a/usr.bin/procstat/procstat_files.c
+++ b/usr.bin/procstat/procstat_files.c
@@ -149,6 +149,7 @@ static struct cap_desc {
{ CAP_FCHFLAGS, "cf" },
{ CAP_FCHMOD, "cm" },
{ CAP_FCHOWN, "cn" },
+ { CAP_FCHROOT, "ct" },
{ CAP_FCNTL, "fc" },
{ CAP_FLOCK, "fl" },
{ CAP_FPATHCONF, "fp" },