The branch stable/15 has been updated by jamie: URL: https://cgit.FreeBSD.org/src/commit/?id=5df0b57b74e2d6f04eb5cbbfa2aeb07f35c2b288
commit 5df0b57b74e2d6f04eb5cbbfa2aeb07f35c2b288 Author: Jamie Gritton <ja...@freebsd.org> AuthorDate: 2025-09-10 23:21:11 +0000 Commit: Jamie Gritton <ja...@freebsd.org> CommitDate: 2025-09-13 22:38:10 +0000 MFC jaildesc: remove file-mode-based access controls Jail descriptors were given a file-like mode, user, and group, for the purpose of controlling how the descriptor may be used. This is too far removed from the file paradigm to make sense. Remove it in favor of a better access control method to be added, such as Capsicum. Also add missing code in jaildesc_fill_kinfo. Reported by: crest at rlwinm.de, kib MFC after: 3 days (cherry picked from commit d81b337d690c971d60c731494795ee4b81fb929e) --- lib/libsys/jail.2 | 67 ------------------------------ sys/kern/kern_jail.c | 72 ++++---------------------------- sys/kern/kern_jaildesc.c | 106 +++++++++++------------------------------------ sys/sys/jaildesc.h | 8 ++-- 4 files changed, 36 insertions(+), 217 deletions(-) diff --git a/lib/libsys/jail.2 b/lib/libsys/jail.2 index a2640071d1f0..d3f871608c1d 100644 --- a/lib/libsys/jail.2 +++ b/lib/libsys/jail.2 @@ -340,31 +340,6 @@ work the same as and .Fn jail_remove , except that they operate on the jail referred to by the passed descriptor. -.Pp -Jail operations via descriptors can be done by processes that do not -normally have permission to see or affect the jail, -as long as they are allowed by the file permissions of the jail -descriptor itself. -These permissions can be changed by the descriptor owner via -.Xr fchmod 2 -and -.Xr fchown 2 . -.Fn jail_get -requires read permission, -.Fn jail_set -and -.Fn jail_remove -require write permission, -and -.Fn jail_attach -requires execute permission. -Also, use of a descriptor with the -.Dv JAIL_AT_DESC -flag requires execute permission. -An owning descriptor is identified by the -.Em sticky bit , -which may also be changed via -.Xr fchmod 2 . .Sh RETURN VALUES If successful, .Fn jail , @@ -402,22 +377,6 @@ The system call will fail if: .Bl -tag -width Er -.It Bq Er EACCES -Write permission is denied on the jail descriptor in the -.Va desc -parameter, -and the -.Dv JAIL_USE_DESC -flag was set. -.It Bq Er EACCES -Execute permission is denied on the jail descriptor in the -.Va desc -parameter, -and either the -.Dv JAIL_AT_DESC -or -.Dv JAIL_ATTACH -flag was set. .It Bq Er EPERM This process is not allowed to create a jail, either because it is not the super-user, or because it would exceed the jail's @@ -505,24 +464,6 @@ The system call will fail if: .Bl -tag -width Er -.It Bq Er EACCES -Read permission is denied on the jail descriptor in the -.Va desc -parameter, -and the -.Dv JAIL_USE_DESC -flag was set. -.It Bq Er EACCES -Execute permission is denied on the jail descriptor in the -.Va desc -parameter, -and the -.Dv JAIL_AT_DESC -flag was set. -.It Bq Er EFAULT -.Fa Iov , -or one of the addresses contained within it, -points to an address outside the allocated address space of the process. .It Bq Er ENOENT The jail referred to by a .Va jid @@ -597,14 +538,6 @@ will fail if: The .Fa fd argument is not a valid jail descriptor. -.It Bq Er EACCES -Permission is denied on the jail descriptor -.Po -execute permission for -.Fn jail_attach_fd , -or write permission for -.Fn jail_remove_fd -.Pc . .It Bq Er EPERM The jail descriptor was created by a user other than the super-user. .It Bq Er EINVAL diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 51a8b5cc0465..3d18b03119ff 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -991,7 +991,6 @@ int kern_jail_set(struct thread *td, struct uio *optuio, int flags) { struct file *jfp_out; - struct jaildesc *desc_in; struct nameidata nd; #ifdef INET struct prison_ip *ip4; @@ -1095,24 +1094,13 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * descriptor's prison. */ prison_free(mypr); - error = jaildesc_find(td, jfd_in, &desc_in, &mypr, - NULL); + error = jaildesc_find(td, jfd_in, &mypr, NULL); if (error != 0) { vfs_opterror(opts, error == ENOENT ? "descriptor to dead jail" : "not a jail descriptor"); goto done_errmsg; } - /* - * Check file permissions using the current - * credentials, and operation permissions - * using the descriptor's credentials. - */ - error = vaccess(VREG, desc_in->jd_mode, desc_in->jd_uid, - desc_in->jd_gid, VEXEC, td->td_ucred); - JAILDESC_UNLOCK(desc_in); - if (error != 0) - goto done_free; if ((flags & JAIL_CREATE) && mypr->pr_childmax == 0) { error = EPERM; goto done_free; @@ -1516,7 +1504,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) } if (flags & JAIL_USE_DESC) { /* Get the jail from its descriptor. */ - error = jaildesc_find(td, jfd_in, &desc_in, &pr, &jdcred); + error = jaildesc_find(td, jfd_in, &pr, &jdcred); if (error) { vfs_opterror(opts, error == ENOENT ? "descriptor to dead jail" : @@ -1524,19 +1512,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) goto done_deref; } drflags |= PD_DEREF; - /* - * Check file permissions using the current credentials, - * and operation permissions using the descriptor's - * credentials. - */ - error = vaccess(VREG, desc_in->jd_mode, desc_in->jd_uid, - desc_in->jd_gid, VWRITE, td->td_ucred); - if (error == 0 && (flags & JAIL_ATTACH)) - error = vaccess(VREG, desc_in->jd_mode, desc_in->jd_uid, - desc_in->jd_gid, VEXEC, td->td_ucred); - JAILDESC_UNLOCK(desc_in); - if (error == 0) - error = priv_check_cred(jdcred, PRIV_JAIL_SET); + error = priv_check_cred(jdcred, PRIV_JAIL_SET); if (error == 0 && (flags & JAIL_ATTACH)) error = priv_check_cred(jdcred, PRIV_JAIL_ATTACH); crfree(jdcred); @@ -2500,7 +2476,6 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) { struct bool_flags *bf; struct file *jfp_out; - struct jaildesc *desc_in; struct jailsys_flags *jsf; struct prison *pr, *mypr; struct vfsopt *opt; @@ -2547,7 +2522,7 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) } if (flags & JAIL_USE_DESC) { /* Get the jail from its descriptor. */ - error = jaildesc_find(td, jfd_in, &desc_in, &pr, NULL); + error = jaildesc_find(td, jfd_in, &pr, NULL); if (error) { vfs_opterror(opts, error == ENOENT ? "descriptor to dead jail" : @@ -2555,11 +2530,6 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) goto done; } drflags |= PD_DEREF; - error = vaccess(VREG, desc_in->jd_mode, desc_in->jd_uid, - desc_in->jd_gid, VREAD, td->td_ucred); - JAILDESC_UNLOCK(desc_in); - if (error != 0) - goto done; mtx_lock(&pr->pr_mtx); drflags |= PD_LOCKED; if (!(prison_isalive(pr) || (flags & JAIL_DYING))) { @@ -2573,19 +2543,13 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) if (flags & JAIL_AT_DESC) { /* Look up jails based on the descriptor's prison. */ prison_free(mypr); - error = jaildesc_find(td, jfd_in, &desc_in, &mypr, - NULL); + error = jaildesc_find(td, jfd_in, &mypr, NULL); if (error != 0) { vfs_opterror(opts, error == ENOENT ? "descriptor to dead jail" : "not a jail descriptor"); goto done; } - error = vaccess(VREG, desc_in->jd_mode, desc_in->jd_uid, - desc_in->jd_gid, VEXEC, td->td_ucred); - JAILDESC_UNLOCK(desc_in); - if (error != 0) - goto done; } if (flags & (JAIL_GET_DESC | JAIL_OWN_DESC)) { /* Allocate a jail descriptor to return later. */ @@ -2916,23 +2880,14 @@ sys_jail_remove(struct thread *td, struct jail_remove_args *uap) int sys_jail_remove_jd(struct thread *td, struct jail_remove_jd_args *uap) { - struct jaildesc *jd; struct prison *pr; struct ucred *jdcred; int error; - error = jaildesc_find(td, uap->fd, &jd, &pr, &jdcred); + error = jaildesc_find(td, uap->fd, &pr, &jdcred); if (error) return (error); - /* - * Check file permissions using the current credentials, and - * operation permissions using the descriptor's credentials. - */ - error = vaccess(VREG, jd->jd_mode, jd->jd_uid, jd->jd_gid, VWRITE, - td->td_ucred); - JAILDESC_UNLOCK(jd); - if (error == 0) - error = priv_check_cred(jdcred, PRIV_JAIL_REMOVE); + error = priv_check_cred(jdcred, PRIV_JAIL_REMOVE); crfree(jdcred); if (error) { prison_free(pr); @@ -3002,26 +2957,17 @@ sys_jail_attach(struct thread *td, struct jail_attach_args *uap) int sys_jail_attach_jd(struct thread *td, struct jail_attach_jd_args *uap) { - struct jaildesc *jd; struct prison *pr; struct ucred *jdcred; int drflags, error; sx_slock(&allprison_lock); drflags = PD_LIST_SLOCKED; - error = jaildesc_find(td, uap->fd, &jd, &pr, &jdcred); + error = jaildesc_find(td, uap->fd, &pr, &jdcred); if (error) goto fail; drflags |= PD_DEREF; - /* - * Check file permissions using the current credentials, and - * operation permissions using the descriptor's credentials. - */ - error = vaccess(VREG, jd->jd_mode, jd->jd_uid, jd->jd_gid, VEXEC, - td->td_ucred); - JAILDESC_UNLOCK(jd); - if (error == 0) - error = priv_check_cred(jdcred, PRIV_JAIL_ATTACH); + error = priv_check_cred(jdcred, PRIV_JAIL_ATTACH); crfree(jdcred); if (error) goto fail; diff --git a/sys/kern/kern_jaildesc.c b/sys/kern/kern_jaildesc.c index 72e2845aaf42..c9e80f5d8941 100644 --- a/sys/kern/kern_jaildesc.c +++ b/sys/kern/kern_jaildesc.c @@ -41,14 +41,13 @@ #include <sys/sysproto.h> #include <sys/systm.h> #include <sys/ucred.h> +#include <sys/user.h> #include <sys/vnode.h> MALLOC_DEFINE(M_JAILDESC, "jaildesc", "jail descriptors"); static fo_stat_t jaildesc_stat; static fo_close_t jaildesc_close; -static fo_chmod_t jaildesc_chmod; -static fo_chown_t jaildesc_chown; static fo_fill_kinfo_t jaildesc_fill_kinfo; static fo_cmp_t jaildesc_cmp; @@ -61,8 +60,8 @@ static struct fileops jaildesc_ops = { .fo_kqfilter = invfo_kqfilter, .fo_stat = jaildesc_stat, .fo_close = jaildesc_close, - .fo_chmod = jaildesc_chmod, - .fo_chown = jaildesc_chown, + .fo_chmod = invfo_chmod, + .fo_chown = invfo_chown, .fo_sendfile = invfo_sendfile, .fo_fill_kinfo = jaildesc_fill_kinfo, .fo_cmp = jaildesc_cmp, @@ -70,13 +69,13 @@ static struct fileops jaildesc_ops = { }; /* - * Given a jail descriptor number, return the jaildesc, its prison, - * and its credential. The jaildesc will be returned locked, and - * prison and the credential will be returned held. + * Given a jail descriptor number, return its prison and/or its + * credential. They are returned held, and will need to be released + * by the caller. */ int -jaildesc_find(struct thread *td, int fd, struct jaildesc **jdp, - struct prison **prp, struct ucred **ucredp) +jaildesc_find(struct thread *td, int fd, struct prison **prp, + struct ucred **ucredp) { struct file *fp; struct jaildesc *jd; @@ -98,12 +97,11 @@ jaildesc_find(struct thread *td, int fd, struct jaildesc **jdp, JAILDESC_UNLOCK(jd); goto out; } - prison_hold(pr); - *prp = pr; - if (jdp != NULL) - *jdp = jd; - else - JAILDESC_UNLOCK(jd); + if (prp != NULL) { + prison_hold(pr); + *prp = pr; + } + JAILDESC_UNLOCK(jd); if (ucredp != NULL) *ucredp = crhold(fp->f_cred); out: @@ -122,15 +120,12 @@ jaildesc_alloc(struct thread *td, struct file **fpp, int *fdp, int owning) struct file *fp; struct jaildesc *jd; int error; - mode_t mode; if (owning) { error = priv_check(td, PRIV_JAIL_REMOVE); if (error != 0) return (error); - mode = S_ISTXT; - } else - mode = 0; + } jd = malloc(sizeof(*jd), M_JAILDESC, M_WAITOK | M_ZERO); error = falloc_caps(td, &fp, fdp, 0, NULL); if (error != 0) { @@ -140,11 +135,8 @@ jaildesc_alloc(struct thread *td, struct file **fpp, int *fdp, int owning) finit(fp, priv_check_cred(fp->f_cred, PRIV_JAIL_SET) == 0 ? FREAD | FWRITE : FREAD, DTYPE_JAILDESC, jd, &jaildesc_ops); JAILDESC_LOCK_INIT(jd); - jd->jd_uid = fp->f_cred->cr_uid; - jd->jd_gid = fp->f_cred->cr_gid; - jd->jd_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | mode | - (priv_check(td, PRIV_JAIL_SET) == 0 ? S_IWUSR | S_IXUSR : 0) | - (priv_check(td, PRIV_JAIL_ATTACH) == 0 ? S_IXUSR : 0); + if (owning) + jd->jd_flags |= JDF_OWNING; *fpp = fp; return (0); } @@ -206,7 +198,7 @@ jaildesc_close(struct file *fp, struct thread *td) */ prison_hold(pr); JAILDESC_UNLOCK(jd); - if (jd->jd_mode & S_ISTXT) { + if (jd->jd_flags & JDF_OWNING) { sx_xlock(&allprison_lock); prison_lock(pr); if (jd->jd_prison != NULL) { @@ -246,10 +238,8 @@ jaildesc_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) jd = fp->f_data; JAILDESC_LOCK(jd); if (jd->jd_prison != NULL) { - sb->st_ino = jd->jd_prison ? jd->jd_prison->pr_id : 0; - sb->st_uid = jd->jd_uid; - sb->st_gid = jd->jd_gid; - sb->st_mode = jd->jd_mode; + sb->st_ino = jd->jd_prison->pr_id; + sb->st_mode = S_IFREG | S_IRWXU; } else sb->st_mode = S_IFREG; JAILDESC_UNLOCK(jd); @@ -257,63 +247,15 @@ jaildesc_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) } static int -jaildesc_chmod(struct file *fp, mode_t mode, struct ucred *active_cred, - struct thread *td) -{ - struct jaildesc *jd; - int error; - - /* Reject permissions that the creator doesn't have. */ - if (((mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && - priv_check_cred(fp->f_cred, PRIV_JAIL_SET) != 0) || - ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) && - priv_check_cred(fp->f_cred, PRIV_JAIL_ATTACH) != 0 && - priv_check_cred(fp->f_cred, PRIV_JAIL_SET) != 0) || - ((mode & S_ISTXT) && - priv_check_cred(fp->f_cred, PRIV_JAIL_REMOVE) != 0)) - return (EPERM); - if (mode & (S_ISUID | S_ISGID)) - return (EINVAL); - jd = fp->f_data; - JAILDESC_LOCK(jd); - error = vaccess(VREG, jd->jd_mode, jd->jd_uid, jd->jd_gid, VADMIN, - active_cred); - if (error == 0) - jd->jd_mode = S_IFREG | (mode & ALLPERMS); - JAILDESC_UNLOCK(jd); - return (error); -} - -static int -jaildesc_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, - struct thread *td) +jaildesc_fill_kinfo(struct file *fp, struct kinfo_file *kif, + struct filedesc *fdp) { struct jaildesc *jd; - int error; - error = 0; jd = fp->f_data; - JAILDESC_LOCK(jd); - if (uid == (uid_t)-1) - uid = jd->jd_uid; - if (gid == (gid_t)-1) - gid = jd->jd_gid; - if ((uid != jd->jd_uid && uid != active_cred->cr_uid) || - (gid != jd->jd_gid && !groupmember(gid, active_cred))) - error = priv_check_cred(active_cred, PRIV_VFS_CHOWN); - if (error == 0) { - jd->jd_uid = uid; - jd->jd_gid = gid; - } - JAILDESC_UNLOCK(jd); - return (error); -} - -static int -jaildesc_fill_kinfo(struct file *fp, struct kinfo_file *kif, - struct filedesc *fdp) -{ - return (EINVAL); + kif->kf_type = KF_TYPE_JAILDESC; + kif->kf_un.kf_jail.kf_jid = jd->jd_prison ? jd->jd_prison->pr_id : 0; + return (0); } static int diff --git a/sys/sys/jaildesc.h b/sys/sys/jaildesc.h index 4bed1ab3b88a..2451b04f7302 100644 --- a/sys/sys/jaildesc.h +++ b/sys/sys/jaildesc.h @@ -54,9 +54,6 @@ struct jaildesc { LIST_ENTRY(jaildesc) jd_list; /* (d,p) this prison's descs */ struct prison *jd_prison; /* (d) the prison */ struct mtx jd_lock; - uid_t jd_uid; /* (d) nominal file owner */ - gid_t jd_gid; /* (d) nominal file group */ - mode_t jd_mode; /* (d) descriptor permissions */ unsigned jd_flags; /* (d) JDF_* flags */ }; @@ -73,9 +70,10 @@ struct jaildesc { * Flags for the jd_flags field */ #define JDF_REMOVED 0x00000002 /* jail was removed */ +#define JDF_OWNING 0x00000004 /* closing descriptor removes jail */ -int jaildesc_find(struct thread *td, int fd, struct jaildesc **jdp, - struct prison **prp, struct ucred **ucredp); +int jaildesc_find(struct thread *td, int fd, struct prison **prp, + struct ucred **ucredp); int jaildesc_alloc(struct thread *td, struct file **fpp, int *fdp, int owning); void jaildesc_set_prison(struct file *jd, struct prison *pr); void jaildesc_prison_cleanup(struct prison *pr);