Skip to content

Commit b165e9e

Browse files
committed
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
1 parent 347dd05 commit b165e9e

11 files changed

Lines changed: 120 additions & 28 deletions

File tree

include/unistd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ int exect(const char *, char * const *, char * const *);
507507
int execvP(const char *, const char *, char * const *);
508508
int execvpe(const char *, char * const *, char * const *);
509509
int feature_present(const char *);
510+
int fchroot(int);
510511
char *fflagstostr(u_long);
511512
int getdomainname(char *, int);
512513
int getentropy(void *, size_t);

lib/libsys/Makefile.sys

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ MLINKS+=chmod.2 fchmod.2 \
399399
MLINKS+=chown.2 fchown.2 \
400400
chown.2 fchownat.2 \
401401
chown.2 lchown.2
402+
MLINKS+=chroot.2 fchroot.2
402403
MLINKS+=clock_gettime.2 clock_getres.2 \
403404
clock_gettime.2 clock_settime.2
404405
MLINKS+=closefrom.2 close_range.2

lib/libsys/Symbol.sys.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ FBSD_1.7 {
378378
};
379379

380380
FBSD_1.8 {
381+
fchroot;
381382
getrlimitusage;
382383
kcmp;
383384
};

lib/libsys/chroot.2

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,21 @@
2525
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2626
.\" SUCH DAMAGE.
2727
.\"
28-
.Dd September 29, 2020
28+
.Dd July 15, 2024
2929
.Dt CHROOT 2
3030
.Os
3131
.Sh NAME
32-
.Nm chroot
32+
.Nm chroot ,
33+
.Nm fchroot
3334
.Nd change root directory
3435
.Sh LIBRARY
3536
.Lb libc
3637
.Sh SYNOPSIS
3738
.In unistd.h
3839
.Ft int
3940
.Fn chroot "const char *dirname"
41+
.Ft int
42+
.Fn fchroot "int fd"
4043
.Sh DESCRIPTION
4144
The
4245
.Fa dirname
@@ -92,6 +95,12 @@ will bypass the check for open directories,
9295
mimicking the historic insecure behavior of
9396
.Fn chroot
9497
still present on other systems.
98+
.Pp
99+
The
100+
.Fn fchroot
101+
system call is identical to
102+
.Fn chroot
103+
except it takes a file descriptor instead of path.
95104
.Sh RETURN VALUES
96105
.Rv -std
97106
.Sh ERRORS
@@ -124,6 +133,29 @@ An I/O error occurred while reading from or writing to the file system.
124133
.It Bq Er EINTEGRITY
125134
Corrupted data was detected while reading from the file system.
126135
.El
136+
.Pp
137+
The
138+
.Fn fchroot
139+
system call
140+
will fail and the root directory will be unchanged if:
141+
.Bl -tag -width Er
142+
.It Bq Er EACCES
143+
Search permission is denied for the directory referenced by the
144+
file descriptor.
145+
.It Bq Er EBADF
146+
The argument
147+
.Fa fd
148+
is not a valid file descriptor.
149+
.It Bq Er EIO
150+
An I/O error occurred while reading from or writing to the file system.
151+
.It Bq Er EINTEGRITY
152+
Corrupted data was detected while reading from the file system.
153+
.It Bq Er ENOTDIR
154+
The file descriptor does not reference a directory.
155+
.It Bq Er EPERM
156+
The effective user ID is not the super-user, or one or more
157+
filedescriptors are open directories.
158+
.El
127159
.Sh SEE ALSO
128160
.Xr chdir 2 ,
129161
.Xr jail 2
@@ -137,6 +169,10 @@ It was marked as
137169
in
138170
.St -susv2 ,
139171
and was removed in subsequent standards.
172+
The
173+
.Fn fchroot
174+
system call first appeared in
175+
.Fx 15.0 .
140176
.Sh BUGS
141177
If the process is able to change its working directory to the target
142178
directory, but another access control check fails (such as a check for

share/man/man4/rights.4

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3131
.\" SUCH DAMAGE.
3232
.\"
33-
.Dd April 27, 2024
33+
.Dd May 1, 2024
3434
.Dt RIGHTS 4
3535
.Os
3636
.Sh NAME
@@ -209,6 +209,9 @@ An alias to
209209
.Dv CAP_FCHOWN
210210
and
211211
.Dv CAP_LOOKUP .
212+
.It Dv CAP_FCHROOT
213+
Permit
214+
.Xr fchroot 2 .
212215
.It Dv CAP_FCNTL
213216
Permit
214217
.Xr fcntl 2 .

sys/kern/subr_capability.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ __read_mostly cap_rights_t cap_fchdir_rights;
5959
__read_mostly cap_rights_t cap_fchflags_rights;
6060
__read_mostly cap_rights_t cap_fchmod_rights;
6161
__read_mostly cap_rights_t cap_fchown_rights;
62+
__read_mostly cap_rights_t cap_fchroot_rights;
6263
__read_mostly cap_rights_t cap_fcntl_rights;
6364
__read_mostly cap_rights_t cap_fexecve_rights;
6465
__read_mostly cap_rights_t cap_flock_rights;
@@ -108,6 +109,7 @@ cap_rights_sysinit(void *arg)
108109
cap_rights_init_one(&cap_fchflags_rights, CAP_FCHFLAGS);
109110
cap_rights_init_one(&cap_fchmod_rights, CAP_FCHMOD);
110111
cap_rights_init_one(&cap_fchown_rights, CAP_FCHOWN);
112+
cap_rights_init_one(&cap_fchroot_rights, CAP_FCHROOT);
111113
cap_rights_init_one(&cap_fcntl_rights, CAP_FCNTL);
112114
cap_rights_init_one(&cap_fexecve_rights, CAP_FEXECVE);
113115
cap_rights_init_one(&cap_flock_rights, CAP_FLOCK);

sys/kern/syscalls.master

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3341,5 +3341,10 @@
33413341
_Out_ rlim_t *res
33423342
);
33433343
}
3344+
590 AUE_NULL STD {
3345+
int fchroot(
3346+
int fd
3347+
);
3348+
}
33443349

33453350
; vim: syntax=off

sys/kern/vfs_syscalls.c

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -967,18 +967,13 @@ static int unprivileged_chroot = 0;
967967
SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_chroot, CTLFLAG_RW,
968968
&unprivileged_chroot, 0,
969969
"Unprivileged processes can use chroot(2)");
970+
970971
/*
971-
* Change notion of root (``/'') directory.
972+
* Takes locked vnode, unlocks it before returning.
972973
*/
973-
#ifndef _SYS_SYSPROTO_H_
974-
struct chroot_args {
975-
char *path;
976-
};
977-
#endif
978-
int
979-
sys_chroot(struct thread *td, struct chroot_args *uap)
974+
static int
975+
kern_chroot(struct thread *td, struct vnode *vp)
980976
{
981-
struct nameidata nd;
982977
struct proc *p;
983978
int error;
984979

@@ -989,30 +984,75 @@ sys_chroot(struct thread *td, struct chroot_args *uap)
989984
if (unprivileged_chroot == 0 ||
990985
(p->p_flag2 & P2_NO_NEW_PRIVS) == 0) {
991986
PROC_UNLOCK(p);
992-
return (error);
987+
goto e_vunlock;
993988
}
994989
PROC_UNLOCK(p);
995990
}
996-
NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
997-
UIO_USERSPACE, uap->path);
998-
error = namei(&nd);
999-
if (error != 0)
1000-
return (error);
1001-
NDFREE_PNBUF(&nd);
1002-
error = change_dir(nd.ni_vp, td);
991+
992+
error = change_dir(vp, td);
1003993
if (error != 0)
1004994
goto e_vunlock;
1005995
#ifdef MAC
1006-
error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp);
996+
error = mac_vnode_check_chroot(td->td_ucred, vp);
1007997
if (error != 0)
1008998
goto e_vunlock;
1009999
#endif
1010-
VOP_UNLOCK(nd.ni_vp);
1011-
error = pwd_chroot(td, nd.ni_vp);
1012-
vrele(nd.ni_vp);
1000+
VOP_UNLOCK(vp);
1001+
error = pwd_chroot(td, vp);
1002+
vrele(vp);
10131003
return (error);
10141004
e_vunlock:
1015-
vput(nd.ni_vp);
1005+
vput(vp);
1006+
return (error);
1007+
}
1008+
1009+
/*
1010+
* Change notion of root (``/'') directory.
1011+
*/
1012+
#ifndef _SYS_SYSPROTO_H_
1013+
struct chroot_args {
1014+
char *path;
1015+
};
1016+
#endif
1017+
int
1018+
sys_chroot(struct thread *td, struct chroot_args *uap)
1019+
{
1020+
struct nameidata nd;
1021+
int error;
1022+
1023+
NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
1024+
UIO_USERSPACE, uap->path);
1025+
error = namei(&nd);
1026+
if (error != 0)
1027+
return (error);
1028+
NDFREE_PNBUF(&nd);
1029+
error = kern_chroot(td, nd.ni_vp);
1030+
return (error);
1031+
}
1032+
1033+
/*
1034+
* Change notion of root directory to a given file descriptor.
1035+
*/
1036+
#ifndef _SYS_SYSPROTO_H_
1037+
struct fchroot_args {
1038+
int fd;
1039+
};
1040+
#endif
1041+
int
1042+
sys_fchroot(struct thread *td, struct fchroot_args *uap)
1043+
{
1044+
struct vnode *vp;
1045+
struct file *fp;
1046+
int error;
1047+
1048+
error = getvnode_path(td, uap->fd, &cap_fchroot_rights, &fp);
1049+
if (error != 0)
1050+
return (error);
1051+
vp = fp->f_vnode;
1052+
vrefact(vp);
1053+
fdrop(fp, td);
1054+
vn_lock(vp, LK_SHARED | LK_RETRY);
1055+
error = kern_chroot(td, vp);
10161056
return (error);
10171057
}
10181058

sys/sys/caprights.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ extern cap_rights_t cap_fchdir_rights;
6666
extern cap_rights_t cap_fchflags_rights;
6767
extern cap_rights_t cap_fchmod_rights;
6868
extern cap_rights_t cap_fchown_rights;
69+
extern cap_rights_t cap_fchroot_rights;
6970
extern cap_rights_t cap_fcntl_rights;
7071
extern cap_rights_t cap_fexecve_rights;
7172
extern cap_rights_t cap_flock_rights;

sys/sys/capsicum.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@
201201
/* Allows for renameat(2) (target directory descriptor). */
202202
#define CAP_RENAMEAT_TARGET (CAP_LOOKUP | 0x0000040000000000ULL)
203203

204+
/* Allows for fchroot(2). */
205+
#define CAP_FCHROOT CAPRIGHT(0, 0x0000080000000000ULL)
206+
204207
#define CAP_SOCK_CLIENT \
205208
(CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \
206209
CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN)
@@ -210,11 +213,9 @@
210213
CAP_SETSOCKOPT | CAP_SHUTDOWN)
211214

212215
/* All used bits for index 0. */
213-
#define CAP_ALL0 CAPRIGHT(0, 0x000007FFFFFFFFFFULL)
216+
#define CAP_ALL0 CAPRIGHT(0, 0x00000FFFFFFFFFFFULL)
214217

215218
/* Available bits for index 0. */
216-
#define CAP_UNUSED0_44 CAPRIGHT(0, 0x0000080000000000ULL)
217-
/* ... */
218219
#define CAP_UNUSED0_57 CAPRIGHT(0, 0x0100000000000000ULL)
219220

220221
/* INDEX 1 */

0 commit comments

Comments
 (0)