diff options
-rw-r--r-- | sys/compat/linux/linux_stats.c | 5 | ||||
-rw-r--r-- | sys/kern/kern_jail.c | 97 | ||||
-rw-r--r-- | sys/kern/vfs_extattr.c | 30 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 30 | ||||
-rw-r--r-- | sys/sys/jail.h | 5 |
5 files changed, 143 insertions, 24 deletions
diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 7ddb192..da08a10 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -331,8 +331,9 @@ linux_ustat(struct thread *td, struct linux_ustat_args *args) if (dev != NULL && vfinddev(dev, &vp)) { if (vp->v_mount == NULL) return (EINVAL); - if (!prison_check_mount(td->td_ucred, vp->v_mount)) - return (EINVAL); + error = prison_canseemount(td->td_ucred, vp->v_mount); + if (error) + return (error); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, vp->v_mount); if (error) diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index e125622..9ca85c6 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -26,6 +26,7 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/mutex.h> #include <sys/namei.h> +#include <sys/mount.h> #include <sys/queue.h> #include <sys/socket.h> #include <sys/syscallsubr.h> @@ -55,10 +56,10 @@ SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW, &jail_sysvipc_allowed, 0, "Processes in jail can use System V IPC primitives"); -int jail_getfsstatroot_only = 1; -SYSCTL_INT(_security_jail, OID_AUTO, getfsstatroot_only, CTLFLAG_RW, - &jail_getfsstatroot_only, 0, - "Processes see only their root file system in getfsstat()"); +static int jail_enforce_statfs = 2; +SYSCTL_INT(_security_jail, OID_AUTO, enforce_statfs, CTLFLAG_RW, + &jail_enforce_statfs, 0, + "Processes in jail cannot see all mounted file systems"); int jail_allow_raw_sockets = 0; SYSCTL_INT(_security_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW, @@ -435,18 +436,92 @@ getcredhostname(struct ucred *cred, char *buf, size_t size) } /* - * Return 1 if the passed credential can "see" the passed mountpoint - * when performing a getfsstat(); otherwise, 0. + * Determine whether the subject represented by cred can "see" + * status of a mount point. + * Returns: 0 for permitted, ENOENT otherwise. + * XXX: This function should be called cr_canseemount() and should be + * placed in kern_prot.c. */ int -prison_check_mount(struct ucred *cred, struct mount *mp) +prison_canseemount(struct ucred *cred, struct mount *mp) { + struct prison *pr; + struct statfs *sp; + size_t len; - if (jail_getfsstatroot_only && cred->cr_prison != NULL) { - if (cred->cr_prison->pr_root->v_mount != mp) - return (0); + if (!jailed(cred) || jail_enforce_statfs == 0) + return (0); + pr = cred->cr_prison; + if (pr->pr_root->v_mount == mp) + return (0); + if (jail_enforce_statfs == 2) + return (ENOENT); + /* + * If jail's chroot directory is set to "/" we should be able to see + * all mount-points from inside a jail. + * This is ugly check, but this is the only situation when jail's + * directory ends with '/'. + */ + if (strcmp(pr->pr_path, "/") == 0) + return (0); + len = strlen(pr->pr_path); + sp = &mp->mnt_stat; + if (strncmp(pr->pr_path, sp->f_mntonname, len) != 0) + return (ENOENT); + /* + * Be sure that we don't have situation where jail's root directory + * is "/some/path" and mount point is "/some/pathpath". + */ + if (sp->f_mntonname[len] != '\0' && sp->f_mntonname[len] != '/') + return (ENOENT); + return (0); +} + +void +prison_enforce_statfs(struct ucred *cred, struct mount *mp, struct statfs *sp) +{ + char jpath[MAXPATHLEN]; + struct prison *pr; + size_t len; + + if (!jailed(cred) || jail_enforce_statfs == 0) + return; + pr = cred->cr_prison; + if (prison_canseemount(cred, mp) != 0) { + /* Should never happen. */ + bzero(sp->f_mntonname, sizeof(sp->f_mntonname)); + strlcpy(sp->f_mntonname, "[restricted]", + sizeof(sp->f_mntonname)); + return; + } + if (pr->pr_root->v_mount == mp) { + /* + * Clear current buffer data, so we are sure nothing from + * the valid path left there. + */ + bzero(sp->f_mntonname, sizeof(sp->f_mntonname)); + *sp->f_mntonname = '/'; + return; + } + /* + * If jail's chroot directory is set to "/" we should be able to see + * all mount-points from inside a jail. + */ + if (strcmp(pr->pr_path, "/") == 0) + return; + len = strlen(pr->pr_path); + strlcpy(jpath, sp->f_mntonname + len, sizeof(jpath)); + /* + * Clear current buffer data, so we are sure nothing from + * the valid path left there. + */ + bzero(sp->f_mntonname, sizeof(sp->f_mntonname)); + if (*jpath == '\0') { + /* Should never happen. */ + *sp->f_mntonname = '/'; + } else { + strlcpy(sp->f_mntonname, jpath, sizeof(sp->f_mntonname)); } - return (1); } static int diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c index 16975cc..44ed4bf 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -257,6 +257,11 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); + error = prison_canseemount(td->td_ucred, mp); + if (error) { + mtx_unlock(&Giant); + return (error); + } #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) { @@ -271,14 +276,17 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); - mtx_unlock(&Giant); - if (error) + if (error) { + mtx_unlock(&Giant); return (error); + } if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + prison_enforce_statfs(td->td_ucred, mp, &sb); sp = &sb; } + mtx_unlock(&Giant); *buf = *sp; return (0); } @@ -327,6 +335,11 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) mtx_unlock(&Giant); return (EBADF); } + error = prison_canseemount(td->td_ucred, mp); + if (error) { + mtx_unlock(&Giant); + return (error); + } #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) { @@ -342,14 +355,17 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); - mtx_unlock(&Giant); - if (error) + if (error) { + mtx_unlock(&Giant); return (error); + } if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + prison_enforce_statfs(td->td_ucred, mp, &sb); sp = &sb; } + mtx_unlock(&Giant); *buf = *sp; return (0); } @@ -393,7 +409,7 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize, mtx_lock(&Giant); mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { - if (!prison_check_mount(td->td_ucred, mp)) { + if (prison_canseemount(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } @@ -432,6 +448,7 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize, if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + prison_enforce_statfs(td->td_ucred, mp, &sb); sp = &sb; } if (bufseg == UIO_USERSPACE) { @@ -4221,6 +4238,9 @@ kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf) mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); + error = prison_canseemount(td->td_ucred, mp); + if (error) + return (error); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) { diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 16975cc..44ed4bf 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -257,6 +257,11 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); + error = prison_canseemount(td->td_ucred, mp); + if (error) { + mtx_unlock(&Giant); + return (error); + } #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) { @@ -271,14 +276,17 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); - mtx_unlock(&Giant); - if (error) + if (error) { + mtx_unlock(&Giant); return (error); + } if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + prison_enforce_statfs(td->td_ucred, mp, &sb); sp = &sb; } + mtx_unlock(&Giant); *buf = *sp; return (0); } @@ -327,6 +335,11 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) mtx_unlock(&Giant); return (EBADF); } + error = prison_canseemount(td->td_ucred, mp); + if (error) { + mtx_unlock(&Giant); + return (error); + } #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) { @@ -342,14 +355,17 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp, td); - mtx_unlock(&Giant); - if (error) + if (error) { + mtx_unlock(&Giant); return (error); + } if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + prison_enforce_statfs(td->td_ucred, mp, &sb); sp = &sb; } + mtx_unlock(&Giant); *buf = *sp; return (0); } @@ -393,7 +409,7 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize, mtx_lock(&Giant); mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { - if (!prison_check_mount(td->td_ucred, mp)) { + if (prison_canseemount(td->td_ucred, mp) != 0) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } @@ -432,6 +448,7 @@ kern_getfsstat(struct thread *td, struct statfs *buf, size_t bufsize, if (suser(td)) { bcopy(sp, &sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + prison_enforce_statfs(td->td_ucred, mp, &sb); sp = &sb; } if (bufseg == UIO_USERSPACE) { @@ -4221,6 +4238,9 @@ kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf) mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); + error = prison_canseemount(td->td_ucred, mp); + if (error) + return (error); #ifdef MAC error = mac_check_mount_stat(td->td_ucred, mp); if (error) { diff --git a/sys/sys/jail.h b/sys/sys/jail.h index 73d6940..0be8fac 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -98,10 +98,13 @@ extern struct prisonlist allprison; struct ucred; struct mount; struct sockaddr; +struct statfs; int jailed(struct ucred *cred); void getcredhostname(struct ucred *cred, char *, size_t); int prison_check(struct ucred *cred1, struct ucred *cred2); -int prison_check_mount(struct ucred *cred, struct mount *mp); +int prison_canseemount(struct ucred *cred, struct mount *mp); +void prison_enforce_statfs(struct ucred *cred, struct mount *mp, + struct statfs *sp); void prison_free(struct prison *pr); u_int32_t prison_getip(struct ucred *cred); void prison_hold(struct prison *pr); |