mirror of
https://github.com/opnsense/src.git
synced 2026-06-09 08:43:19 -04:00
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
This commit is contained in:
parent
347dd0539f
commit
b165e9e3ea
11 changed files with 123 additions and 31 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -378,6 +378,7 @@ FBSD_1.7 {
|
|||
};
|
||||
|
||||
FBSD_1.8 {
|
||||
fchroot;
|
||||
getrlimitusage;
|
||||
kcmp;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 .
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -3341,5 +3341,10 @@
|
|||
_Out_ rlim_t *res
|
||||
);
|
||||
}
|
||||
590 AUE_NULL STD {
|
||||
int fchroot(
|
||||
int fd
|
||||
);
|
||||
}
|
||||
|
||||
; vim: syntax=off
|
||||
|
|
|
|||
|
|
@ -967,6 +967,45 @@ static int unprivileged_chroot = 0;
|
|||
SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_chroot, CTLFLAG_RW,
|
||||
&unprivileged_chroot, 0,
|
||||
"Unprivileged processes can use chroot(2)");
|
||||
|
||||
/*
|
||||
* Takes locked vnode, unlocks it before returning.
|
||||
*/
|
||||
static int
|
||||
kern_chroot(struct thread *td, struct vnode *vp)
|
||||
{
|
||||
struct proc *p;
|
||||
int error;
|
||||
|
||||
error = priv_check(td, PRIV_VFS_CHROOT);
|
||||
if (error != 0) {
|
||||
p = td->td_proc;
|
||||
PROC_LOCK(p);
|
||||
if (unprivileged_chroot == 0 ||
|
||||
(p->p_flag2 & P2_NO_NEW_PRIVS) == 0) {
|
||||
PROC_UNLOCK(p);
|
||||
goto e_vunlock;
|
||||
}
|
||||
PROC_UNLOCK(p);
|
||||
}
|
||||
|
||||
error = change_dir(vp, td);
|
||||
if (error != 0)
|
||||
goto e_vunlock;
|
||||
#ifdef MAC
|
||||
error = mac_vnode_check_chroot(td->td_ucred, vp);
|
||||
if (error != 0)
|
||||
goto e_vunlock;
|
||||
#endif
|
||||
VOP_UNLOCK(vp);
|
||||
error = pwd_chroot(td, vp);
|
||||
vrele(vp);
|
||||
return (error);
|
||||
e_vunlock:
|
||||
vput(vp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change notion of root (``/'') directory.
|
||||
*/
|
||||
|
|
@ -979,40 +1018,41 @@ int
|
|||
sys_chroot(struct thread *td, struct chroot_args *uap)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct proc *p;
|
||||
int error;
|
||||
|
||||
error = priv_check(td, PRIV_VFS_CHROOT);
|
||||
if (error != 0) {
|
||||
p = td->td_proc;
|
||||
PROC_LOCK(p);
|
||||
if (unprivileged_chroot == 0 ||
|
||||
(p->p_flag2 & P2_NO_NEW_PRIVS) == 0) {
|
||||
PROC_UNLOCK(p);
|
||||
return (error);
|
||||
}
|
||||
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);
|
||||
if (error != 0)
|
||||
goto e_vunlock;
|
||||
#ifdef MAC
|
||||
error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp);
|
||||
if (error != 0)
|
||||
goto e_vunlock;
|
||||
#endif
|
||||
VOP_UNLOCK(nd.ni_vp);
|
||||
error = pwd_chroot(td, nd.ni_vp);
|
||||
vrele(nd.ni_vp);
|
||||
error = kern_chroot(td, nd.ni_vp);
|
||||
return (error);
|
||||
e_vunlock:
|
||||
vput(nd.ni_vp);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue