summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/compat/linux/linux_stats.c5
-rw-r--r--sys/kern/kern_jail.c97
-rw-r--r--sys/kern/vfs_extattr.c30
-rw-r--r--sys/kern/vfs_syscalls.c30
-rw-r--r--sys/sys/jail.h5
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);
OpenPOWER on IntegriCloud