Date: Mon, 10 Mar 2003 19:53:25 +0000 (UTC) From: "Bjoern A.Zeeb" <bzeeb+freebsd@zabbadoz.net> To: FreeBSD-gnats-submit@FreeBSD.org Cc: bzeeb+freebsd@zabbadoz.net Subject: kern/49085: jail statfs and mountpoint information leak fix Message-ID: <20030310195325.639961535F@mail.int.zabbadoz.net>
next in thread | raw e-mail | index | archive | help
>Number: 49085
>Category: kern
>Synopsis: jail statfs and mountpoint information leak fix
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: update
>Submitter-Id: current-users
>Arrival-Date: Mon Mar 10 12:00:36 PST 2003
>Closed-Date:
>Last-Modified:
>Originator: Bjoern A. Zeeb
>Release: FreeBSD 4.7-STABLE i386
>Organization:
Zabbadoz.NeT
>Environment:
System: FreeBSD e0-0.zab2.int.zabbadoz.net 4.7-STABLE FreeBSD 4.7-STABLE #0: Fri Dec 27 22:08:07 UTC 2002 bz@e0-0.zab2.int.zabbadoz.net:/export/src/src/obj/export/src/src/RELENG_4/src/sys/ZAB2-2002122701 i386
>Description:
like in 4 jail in 5 still leak mount point and fs statistics
information (see p.ex. kern/47586).
I know that there is also a kernel module available but as I already
had started to work on this I finished it for those people who
preferr it this way (either take it or close it - gnats submission on
advise that it better can be tracked and will not get lost).
if anybody looks at this please also have a look at various
other jail diffs floating around. thanks
>How-To-Repeat:
>Fix:
attached is a patch for 5.0/HEAD that adds a fine grained option to
control what fs stats can be seen from within jails.
--- description ---
The patch is derived from a private patch done by
Christian Kratzer for RELENG_4 and the public patches
by Oliver Fromme (see kern/47586).
It adds following sysctl option:
security.jail.statfs_restricted
This fine grained option lets you control what and how filesystem
statistcs are seen from within jails:
security.jail.statfs_restricted=0
this is the old behaviour where you could see everything from the
whole host.
security.jail.statfs_restricted=1
this is the default for now. It shows only partitions related to the
jail. If there is no root partition resp. the jail is on a shared
partition a ``fake'' root with the correct values but a stripped
f_mntonname will be shown.
security.jail.statfs_restricted=2
this is almost the same as 1 but it will show a ``full fake'' for a
shared root mount. It will zero out almost all values and write
jail-specific ``fakes'' to the others.
security.jail.statfs_restricted=3
this is almost the same as 1 but it will not show a shared root at
all.
security.jail.statfs_restricted>=4
this will not show anything but procfs, devfs, etc. within the jail.
Be warned that this renders the jail to be almost unusable.
--- /description ---
for some sample output or to download the diff please have look at
http://sources.zabbadoz.net/freebsd/jail.html
--- ./sys/kern/kern_jail.c.orig Mon Feb 3 12:57:06 2003
+++ ./sys/kern/kern_jail.c Tue Feb 4 18:54:55 2003
@@ -49,6 +49,11 @@
&jail_sysvipc_allowed, 0,
"Processes in jail can use System V IPC primitives");
+int jail_statfs_restricted = 1;
+SYSCTL_INT(_security_jail, OID_AUTO, statfs_restricted, CTLFLAG_RW,
+ &jail_statfs_restricted, 0,
+ "Processes in jail may not see all currently mounted file systems");
+
/*
* MPSAFE
*/
@@ -76,6 +81,9 @@
mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF);
pr->pr_securelevel = securelevel;
error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
+ if (error)
+ goto bail;
+ error = copyinstr(j.path, &pr->pr_path, sizeof pr->pr_path, 0);
if (error)
goto bail;
ca.path = j.path;
--- ./sys/kern/vfs_syscalls.c.orig Mon Feb 3 13:12:26 2003
+++ ./sys/kern/vfs_syscalls.c Sun Mar 2 19:31:38 2003
@@ -227,6 +227,10 @@
int error;
struct nameidata nd;
struct statfs sb;
+ int notsu, jrlen;
+
+ if (jail_statfs_restricted >= 4 && jailed(td->td_ucred))
+ return (ENOENT);
NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td);
if ((error = namei(&nd)) != 0)
@@ -244,9 +248,47 @@
if (error)
return (error);
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
- if (suser(td)) {
+ notsu = suser(td);
+ if (notsu || (jail_statfs_restricted && jailed(td->td_ucred))) {
bcopy(sp, &sb, sizeof(sb));
- sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+ if (notsu)
+ sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+
+ if (jail_statfs_restricted && jailed(td->td_ucred)) {
+ jrlen = strlen(td->td_ucred->cr_prison->pr_path);
+
+ if (strlen(mp->mnt_stat.f_mntonname) < jrlen) {
+ switch (jail_statfs_restricted) {
+ case 1:
+ bzero(sb.f_mntonname,
+ sizeof(sb.f_mntonname));
+ *sb.f_mntonname = '/';
+ break;
+ case 2:
+ bzero(&sb, sizeof(sb));
+ strcpy(sb.f_fstypename,
+ "jailfs");
+ strcpy(sb.f_mntfromname,
+ "jailroot");
+ sb.f_flags |= MNT_LOCAL;
+ *sb.f_mntonname = '/';
+ break;
+ case 3:
+ default:
+ return (ENOENT);
+ }
+
+ } else {
+ /* strip jail root (jr) path */
+ bzero(sb.f_mntonname, sizeof(sb.f_mntonname));
+ strcpy(sb.f_mntonname, sp->f_mntonname + jrlen);
+ /* hack for jail root filesystem */
+ if ( ! *sb.f_mntonname ) {
+ *sb.f_mntonname='/';
+ }
+ }
+ }
+
sp = &sb;
}
return (copyout(sp, uap->buf, sizeof(*sp)));
@@ -275,6 +317,10 @@
register struct statfs *sp;
int error;
struct statfs sb;
+ int notsu, jrlen;
+
+ if (jail_statfs_restricted >= 4 && jailed(td->td_ucred))
+ return (EBADF);
if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0)
return (error);
@@ -292,9 +338,47 @@
if (error)
return (error);
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
- if (suser(td)) {
+ notsu = suser(td);
+ if (notsu || (jail_statfs_restricted && jailed(td->td_ucred))) {
bcopy(sp, &sb, sizeof(sb));
- sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+ if (notsu)
+ sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+
+ if (jail_statfs_restricted && jailed(td->td_ucred)) {
+ jrlen = strlen(td->td_ucred->cr_prison->pr_path);
+
+ if (strlen(mp->mnt_stat.f_mntonname) < jrlen) {
+ switch (jail_statfs_restricted) {
+ case 1:
+ bzero(sb.f_mntonname,
+ sizeof(sb.f_mntonname));
+ *sb.f_mntonname = '/';
+ break;
+ case 2:
+ bzero(&sb, sizeof(sb));
+ strcpy(sb.f_fstypename,
+ "jailfs");
+ strcpy(sb.f_mntfromname,
+ "jailroot");
+ sb.f_flags |= MNT_LOCAL;
+ *sb.f_mntonname = '/';
+ break;
+ case 3:
+ default:
+ return (ENOENT);
+ }
+
+ } else {
+ /* strip jail root (jr) path */
+ bzero(sb.f_mntonname, sizeof(sb.f_mntonname));
+ strcpy(sb.f_mntonname, sp->f_mntonname + jrlen);
+ /* hack for jail root filesystem */
+ if ( ! *sb.f_mntonname ) {
+ *sb.f_mntonname='/';
+ }
+ }
+ }
+
sp = &sb;
}
return (copyout(sp, uap->buf, sizeof(*sp)));
@@ -323,10 +407,18 @@
register struct statfs *sp;
caddr_t sfsp;
long count, maxcount, error;
+ struct statfs js, jss;
+ int had_jail_root, base_path_len, mntlen;
+
+ if (jail_statfs_restricted >= 4 && jailed(td->td_ucred))
+ return (ENOENT);
+ if (jail_statfs_restricted == 1 && jailed(td->td_ucred))
+ bzero(&jss, sizeof(jss));
maxcount = uap->bufsize / sizeof(struct statfs);
sfsp = (caddr_t)uap->buf;
count = 0;
+ base_path_len = had_jail_root = 0;
mtx_lock(&mountlist_mtx);
for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) {
#ifdef MAC
@@ -339,6 +431,36 @@
nmp = TAILQ_NEXT(mp, mnt_list);
continue;
}
+ if (jail_statfs_restricted && jailed(td->td_ucred)) {
+ /*
+ * If process is jailed skip files systems that are
+ * not at or below the prison chroot path.
+ */
+ if (strncmp(td->td_ucred->cr_prison->pr_path,
+ mp->mnt_stat.f_mntonname,
+ strlen(td->td_ucred->cr_prison->pr_path))) {
+
+ /*
+ * remember for fake root if appropriate
+ */
+ if (jail_statfs_restricted == 1) {
+ mntlen = strlen(mp->mnt_stat.f_mntonname);
+ if (!strncmp(mp->mnt_stat.f_mntonname,
+ td->td_ucred->cr_prison->pr_path,
+ mntlen) && mntlen > base_path_len) {
+
+ base_path_len = mntlen;
+ bcopy(&mp->mnt_stat, &jss,
+ sizeof(jss));
+ }
+ }
+
+ mtx_lock(&mountlist_mtx);
+ nmp = TAILQ_NEXT(mp, mnt_list);
+ vfs_unbusy(mp, td);
+ continue;
+ }
+ }
if (sfsp && count < maxcount) {
sp = &mp->mnt_stat;
/*
@@ -355,6 +477,25 @@
continue;
}
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+ if (jail_statfs_restricted && jailed(td->td_ucred)) {
+ /*
+ * If this process is jailed we strip away
+ * the chroot path from the f_mntonname
+ * and copy to user space.
+ */
+ bcopy(sp, &js, sizeof(js));
+ bzero(js.f_mntonname, sizeof(js.f_mntonname));
+ strcpy(js.f_mntonname, sp->f_mntonname +
+ strlen(td->td_ucred->cr_prison->pr_path)
+ );
+ /* hack for jail root filesystem */
+ if ( ! *js.f_mntonname ) {
+ *js.f_mntonname='/';
+ ++had_jail_root;
+ }
+
+ sp = &js;
+ }
error = copyout(sp, sfsp, sizeof(*sp));
if (error) {
vfs_unbusy(mp, td);
@@ -368,6 +509,28 @@
vfs_unbusy(mp, td);
}
mtx_unlock(&mountlist_mtx);
+ if (jail_statfs_restricted && jailed(td->td_ucred) && !had_jail_root &&
+ sfsp && count < maxcount) {
+
+ if (jail_statfs_restricted==1 || jail_statfs_restricted==2) {
+ if (jail_statfs_restricted == 1) {
+ bzero(jss.f_mntonname, sizeof(jss.f_mntonname));
+ }
+ if (jail_statfs_restricted == 2) {
+ bzero(&jss, sizeof(jss));
+ strcpy(jss.f_fstypename, "jailfs");
+ strcpy(jss.f_mntfromname, "jailroot");
+ jss.f_flags |= MNT_LOCAL;
+ }
+ *jss.f_mntonname='/';
+
+ error = copyout(&jss, sfsp, sizeof(jss));
+ if (error)
+ return (error);
+ sfsp += sizeof(js);
+ count++;
+ }
+ }
if (sfsp && count > maxcount)
td->td_retval[0] = maxcount;
else
@@ -3748,6 +3911,7 @@
struct statfs sb;
fhandle_t fh;
int error;
+ int notsu, jrlen;
/*
* Must be super user
@@ -3756,6 +3920,9 @@
if (error)
return (error);
+ if (jail_statfs_restricted >= 4 && jailed(td->td_ucred))
+ return (ESTALE);
+
if ((error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t))) != 0)
return (error);
@@ -3774,11 +3941,50 @@
if ((error = VFS_STATFS(mp, sp, td)) != 0)
return (error);
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
- if (suser(td)) {
+ notsu = suser(td);
+ if (notsu || (jail_statfs_restricted && jailed(td->td_ucred))) {
bcopy(sp, &sb, sizeof(sb));
- sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+ if (notsu)
+ sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+
+ if (jail_statfs_restricted && jailed(td->td_ucred)) {
+ jrlen = strlen(td->td_ucred->cr_prison->pr_path);
+
+ if (strlen(mp->mnt_stat.f_mntonname) < jrlen) {
+ switch (jail_statfs_restricted) {
+ case 1:
+ bzero(sb.f_mntonname,
+ sizeof(sb.f_mntonname));
+ *sb.f_mntonname = '/';
+ break;
+ case 2:
+ bzero(&sb, sizeof(sb));
+ strcpy(sb.f_fstypename,
+ "jailfs");
+ strcpy(sb.f_mntfromname,
+ "jailroot");
+ sb.f_flags |= MNT_LOCAL;
+ *sb.f_mntonname = '/';
+ break;
+ case 3:
+ default:
+ return (ENOENT);
+ }
+
+ } else {
+ /* strip jail root (jr) path */
+ bzero(sb.f_mntonname, sizeof(sb.f_mntonname));
+ strcpy(sb.f_mntonname, sp->f_mntonname + jrlen);
+ /* hack for jail root filesystem */
+ if ( ! *sb.f_mntonname ) {
+ *sb.f_mntonname='/';
+ }
+ }
+ }
+
sp = &sb;
}
+
return (copyout(sp, uap->buf, sizeof(*sp)));
}
--- ./sys/sys/jail.h.orig Mon Feb 3 12:41:38 2003
+++ ./sys/sys/jail.h Thu Feb 6 18:20:12 2003
@@ -48,6 +48,7 @@
struct prison {
int pr_ref; /* (p) refcount */
char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */
+ char pr_path[MAXPATHLEN]; /* (c) chroot base */
u_int32_t pr_ip; /* (c) ip addr host */
void *pr_linux; /* (p) linux abi */
int pr_securelevel; /* (p) securelevel */
@@ -62,6 +63,7 @@
extern int jail_set_hostname_allowed;
extern int jail_socket_unixiproute_only;
extern int jail_sysvipc_allowed;
+extern int jail_statfs_restricted;
/*
* Kernel support functions for jail().
--- ./usr.sbin/jail/jail.8.orig Sat Mar 1 20:33:18 2003
+++ ./usr.sbin/jail/jail.8 Sun Mar 2 21:02:15 2003
@@ -370,6 +370,36 @@
with) processes outside of the jail, and in other jails.
As such, this functionality is disabled by default, but can be enabled
by setting this MIB entry to 1.
+.It Va security.jail.statfs_restricted
+This fine grained option lets you control what and how filesystem statistcs
+are seen from within jails:
+.Pp
+.Dl security.jail.statfs_restricted=0
+.Pp
+this is the old behaviour where you could see everything from the whole host.
+.Pp
+.Dl security.jail.statfs_restricted=1
+.Pp
+this is the default for now. It shows only partitions related to the jail.
+If there is no root partition resp. the jail is on a shared partition a ``fake''
+root with the correct values but a stripped
+.Dv f_mntonname
+will be shown.
+.Pp
+.Dl security.jail.statfs_restricted=2
+.Pp
+this is almost the same as 1 but it will show a ``full fake'' for a shared root
+mount. It will zero out almost all values and write jail-specific ``fakes'' to
+the others.
+.Pp
+.Dl security.jail.statfs_restricted=3
+.Pp
+this is almost the same as 1 but it will not show a shared root at all.
+.Pp
+.Dl security.jail.statfs_restricted>=4
+.Pp
+this will not show anything but procfs, devfs, etc. within the jail. Be warned
+that this renders the jail to be almost unusable.
.El
.Sh SEE ALSO
.Xr newaliases 1 ,
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030310195325.639961535F>
