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>
