Skip to content

Commit

Permalink
Add fchroot(2)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
trasz committed Nov 29, 2024
1 parent 347dd05 commit b165e9e
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 28 deletions.
1 change: 1 addition & 0 deletions include/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lib/libsys/Makefile.sys
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/libsys/Symbol.sys.map
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ FBSD_1.7 {
};

FBSD_1.8 {
fchroot;
getrlimitusage;
kcmp;
};
Expand Down
40 changes: 38 additions & 2 deletions lib/libsys/chroot.2
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@
.\" 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
.Sh SYNOPSIS
.In unistd.h
.Ft int
.Fn chroot "const char *dirname"
.Ft int
.Fn fchroot "int fd"
.Sh DESCRIPTION
The
.Fa dirname
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion share/man/man4/rights.4
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 .
Expand Down
2 changes: 2 additions & 0 deletions sys/kern/subr_capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions sys/kern/syscalls.master
Original file line number Diff line number Diff line change
Expand Up @@ -3341,5 +3341,10 @@
_Out_ rlim_t *res
);
}
590 AUE_NULL STD {
int fchroot(
int fd
);
}

; vim: syntax=off
84 changes: 62 additions & 22 deletions sys/kern/vfs_syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions sys/sys/caprights.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions sys/sys/capsicum.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions usr.bin/procstat/procstat_files.c
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down

0 comments on commit b165e9e

Please sign in to comment.