Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 21 Apr 2001 01:11:52 +0200 (CEST)
From:      olli@fromme.com
To:        FreeBSD-gnats-submit@freebsd.org
Cc:        olli@fromme.com
Subject:   kern/26740: [PATCH] jail improvement
Message-ID:  <200104202311.f3KNBqh00491@haluter.fromme.com>

next in thread | raw e-mail | index | archive | help

>Number:         26740
>Category:       kern
>Synopsis:       [PATCH] jail improvement
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Apr 20 16:20:02 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator:     Oliver Fromme
>Release:        FreeBSD 4.3-RC2 alpha
>Organization:
fromme.com
>Environment:
System: FreeBSD haluter.fromme.com 4.3-RC2 FreeBSD 4.3-RC2 #8: Fri Apr 20 23:48:25 CEST 2001 olli@haluter.fromme.com:/sea/src/sys/compile/HALUTER alpha

>Description:

This patch adds a small feature to jails.

Processes within a jails cannot see things outside their
jail (files, processes, sockets) -- however, thay still
can see _all_ mountpoints.  This patch fixes that.  In
particular, it does two things:
-1-  Mounts which are outside of a jail are not returned
     to processes within that jail.
-2-  Mounts within a jail get their jail chroot prefix
     stripped off.  See example below.

The patch modifies statfs(), fstatfs() and getfsstat().

Without my patch, the output of "mount" within a jail on
my test machine looks like this:

   /dev/da0a on / (ufs, local, soft-updates)
   /dev/da0f on /sea (ufs, local, soft-updates)
   /dev/da0e on /var (ufs, local, soft-updates)
   procfs on /proc (procfs, local)
   /dev/vn0c on /usr (ufs, local, soft-updates)
   /dev/vn1c on /jail/olli (ufs, local, read-only)
   /dev/vn2c on /jail/olli (ufs, local, union, soft-updates)
   procfs on /jail/olli/proc (procfs, local)
   /dev/vn3c on /jail/olli/home (ufs, local, read-only)

With my patch, the output is this:

   /dev/vn1c on / (ufs, local, read-only)
   /dev/vn2c on / (ufs, local, union, soft-updates)
   procfs on /proc (procfs, local)
   /dev/vn3c on /home (ufs, local, read-only)

There's a small point to note:  If there aren't any mounts
within the jail, getfsstat() returns 0 (an empty list).
/bin/df handles this well, but /sbin/mount prints
,,error 0``.  I tend to think that that's a bug in
/sbin/mount.  I'll submit a fix if desired.

>How-To-Repeat:

Set up a jail environment somewhere, start the jail and
type "df" or "mount".  You'll see all mountpoints,
including those in other jails and those that aren't in
any jails.

>Fix:

NOTE:  This patch is against -stable.  Unfortunately, I
don't have a machine left for installing -current.
I've had a quick look at the relevant files in -current
in the CVS repository, and I _think_ it shouldn't be too
painful to apply the patch there (change p->pr_prison to
p->p_ucred->cr_prison etc.), but I can't test it myself
on -current.  (It's running perfectly fine on my -stable
box, of course.)


--- src/sys/sys/jail.h.orig	Wed Nov  1 18:58:06 2000
+++ src/sys/sys/jail.h	Sat Apr  7 21:00:39 2001
@@ -30,6 +30,17 @@
 MALLOC_DECLARE(M_PRISON);
 #endif
 
+
+#ifndef MNAMELEN
+/* This is taken from sys/mount.h. */
+#ifdef __i386__
+#define	MNAMELEN	80	/* length of buffer for returned name */
+#endif
+#ifdef __alpha__
+#define	MNAMELEN	72	/* length of buffer for returned name */
+#endif
+#endif
+
 /*
  * This structure describes a prison.  It is pointed to by all struct
  * proc's of the inmates.  pr_ref keeps track of them and is used to
@@ -39,6 +50,7 @@
 struct prison {
 	int		pr_ref;
 	char 		pr_host[MAXHOSTNAMELEN];
+	char		pr_path[MNAMELEN];
 	u_int32_t	pr_ip;
 	void		*pr_linux;
 };
--- src/sys/kern/kern_jail.c.orig	Wed Nov  1 18:58:06 2000
+++ src/sys/kern/kern_jail.c	Sat Apr  7 21:04:31 2001
@@ -69,6 +69,9 @@
 	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;
 	pr->pr_ip = j.ip_number;
 
 	ca.path = j.path;
--- src/sys/kern/vfs_syscalls.c.orig	Tue Mar 20 12:45:01 2001
+++ src/sys/kern/vfs_syscalls.c	Fri Apr 20 23:47:16 2001
@@ -59,6 +59,7 @@
 #include <sys/unistd.h>
 #include <sys/vnode.h>
 #include <sys/proc.h>
+#include <sys/jail.h>
 #include <sys/dirent.h>
 #include <sys/extattr.h>
 
@@ -72,6 +73,8 @@
 
 static int change_dir __P((struct nameidata *ndp, struct proc *p));
 static void checkdirs __P((struct vnode *olddp));
+static int check_prison_mount __P((struct proc *p, struct statfs *sp));
+static void trim_prison_mount __P((struct statfs *sp, int pripl));
 static int chroot_refuse_vdir_fds __P((struct filedesc *fdp));
 static int getutimes __P((const struct timeval *, struct timespec *));
 static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t));
@@ -609,6 +612,62 @@
 }
 
 /*
+ * Check if we're imprisoned and whether the mountpoint
+ * is inside our prison.  Return values:
+ *    -1   if we're not supposed to see this mount.
+ *     0   if it's alright.
+ *    >0   strip that many characters from the beginning of
+ *         the path of the mountpoint.  E.g. if the mount
+ *         point is /foo/bar and the jail is chrooted at /foo,
+ *         4 is be returned, so only /bar will be left.
+ */
+static int
+check_prison_mount(p, sp)
+	struct proc *p;
+	struct statfs *sp;
+{
+	register char *prip;	/* prison path */
+	register int pripl;	/* prison path length */
+
+	if (!p->p_prison)
+		return (0);
+	prip = p->p_prison->pr_path;
+	pripl = strlen(prip);
+	if (pripl > 0 && prip[pripl - 1] == '/')
+		pripl--;	/* ignore trailing slash, if any */
+	/*
+	 * Note that it is not sufficient to check for the
+	 * first <pripl> characters to be the same.
+	 * We also have to make sure that the next character
+	 * in the mountpoint path is either '\0' or '/'.
+	 * Otherwise a jail in "/foo/bar" would be allowed
+	 * to see a mount at "/foo/barbara".
+	 */
+	if (strncmp(prip, sp->f_mntonname, pripl) != 0 ||
+	    (sp->f_mntonname[pripl] != '\0' && sp->f_mntonname[pripl] != '/'))
+		return (-1);	/* not our business */
+	return (pripl);
+}
+
+/*
+ * Remove the jail chroot path from the mountpoint,
+ * so that imprisoned users see everything relative
+ * to their jail chroot.
+ */
+static void
+trim_prison_mount(sp, pripl)
+	struct statfs *sp;
+	int pripl;	/* prison path length */
+{
+	strcpy(sp->f_mntonname, sp->f_mntonname + pripl);
+	/* If there's nothing left, this ought to be "/". */
+	if (sp->f_mntonname[0] == '\0') {
+		sp->f_mntonname[0] = '/';
+		sp->f_mntonname[1] = '\0';
+	}
+}
+
+/*
  * Get filesystem statistics.
  */
 #ifndef _SYS_SYSPROTO_H_
@@ -629,6 +688,8 @@
 	register struct mount *mp;
 	register struct statfs *sp;
 	int error;
+	int pripl;
+	int nonsu;
 	struct nameidata nd;
 	struct statfs sb;
 
@@ -639,13 +700,19 @@
 	sp = &mp->mnt_stat;
 	NDFREE(&nd, NDF_ONLY_PNBUF);
 	vrele(nd.ni_vp);
+	if ((pripl = check_prison_mount(p, sp)) < 0)
+		return (ENOENT);
 	error = VFS_STATFS(mp, sp, p);
 	if (error)
 		return (error);
 	sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
-	if (suser_xxx(p->p_ucred, 0, 0)) {
+	nonsu = suser_xxx(p->p_ucred, 0, 0);
+	if (nonsu || pripl) {
 		bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb));
-		sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+		if (nonsu)
+			sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+		if (pripl)
+			trim_prison_mount(&sb, pripl);
 		sp = &sb;
 	}
 	return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp)));
@@ -673,19 +740,27 @@
 	struct mount *mp;
 	register struct statfs *sp;
 	int error;
+	int pripl;
+	int nonsu;
 	struct statfs sb;
 
 	if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0)
 		return (error);
 	mp = ((struct vnode *)fp->f_data)->v_mount;
 	sp = &mp->mnt_stat;
+	if ((pripl = check_prison_mount(p, sp)) < 0)
+		return (ENOENT);
 	error = VFS_STATFS(mp, sp, p);
 	if (error)
 		return (error);
 	sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
-	if (suser_xxx(p->p_ucred, 0, 0)) {
+	nonsu = suser_xxx(p->p_ucred, 0, 0);
+	if (nonsu || pripl) {
 		bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb));
-		sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+		if (nonsu)
+			sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0;
+		if (pripl)
+			trim_prison_mount(&sb, pripl);
 		sp = &sb;
 	}
 	return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp)));
@@ -714,13 +789,16 @@
 	register struct statfs *sp;
 	caddr_t sfsp;
 	long count, maxcount, error;
+	int pripl;
+	struct statfs sb;
 
 	maxcount = SCARG(uap, bufsize) / sizeof(struct statfs);
 	sfsp = (caddr_t)SCARG(uap, buf);
 	count = 0;
 	simple_lock(&mountlist_slock);
 	for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) {
-		if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) {
+		pripl = check_prison_mount(p, &mp->mnt_stat);
+		if (pripl < 0 || vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) {
 			nmp = TAILQ_NEXT(mp, mnt_list);
 			continue;
 		}
@@ -740,6 +818,11 @@
 				continue;
 			}
 			sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
+			if (pripl) {
+				bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb));
+				trim_prison_mount(&sb, pripl);
+				sp = &sb;
+			}
 			error = copyout((caddr_t)sp, sfsp, sizeof(*sp));
 			if (error) {
 				vfs_unbusy(mp, p);
>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?200104202311.f3KNBqh00491>