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>