--- src/sys/kern/kern_jail.c.orig Mon Sep 3 10:17:12 2001 +++ src/sys/kern/kern_jail.c Wed Feb 27 15:40:43 2002 @@ -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 Mon Jan 7 21:47:34 2002 +++ src/sys/kern/vfs_syscalls.c Wed Feb 27 13:21:06 2002 @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,9 @@ 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, + int specific)); +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)); @@ -356,7 +360,7 @@ /* * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been - * mounted. If so, replace them with the new mount point. + * mounted. If so, replace them with the new mountpoint. */ static void checkdirs(olddp) @@ -611,6 +615,90 @@ } /* + * Check if we're imprisoned and whether the mountpoint is inside of + * 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 mountpoint is /foo/bar and the + * jail is chrooted at /foo, 4 is be returned, so only /bar + * will be left. + * + * The "specific" flag indicates if the request has been issued for + * this specific filesystem (1), or if this is part of an enumeration + * of all filesystems (0). In the former case, we allow it to be + * visible _IF_ it is a prefix of the jail chroot directory. This + * makes "ls" etc. work if the mountpoint of the jail root is outside + * of the jail. In the latter case, this filesystem is hidden. + * Example: The jail chroot is /usr/jail (which is not a separate + * filesystem, but /usr is). Ordinarily, "ls" within the jail would + * fail ("no such file or directory"), because it calls fstatfs() on + * the directory. Similarly for "df ." etc. Therefore, /usr must be + * visible within the jail for fstatfs() (it'll show up as "/" within + * the jail). + */ +static int +check_prison_mount(p, sp, specific) + struct proc *p; + struct statfs *sp; + int specific; +{ + register char *prip; /* prison path */ + register int pripl; /* prison path length */ + + /* short-cut: no prison -> no restriction */ + 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 */ + /* + * First, if this is a specific filesystem request, check if + * the mountpoint is a prefix of the jail root. + * If it is, then strip the whole path, so it'll become "/". + * Otherwise continue with the normal (non-specific) check. + */ + if (specific) { + int mntlen = strlen(sp->f_mntonname); + + if (strncmp(prip, sp->f_mntonname, mntlen) == 0 && + (prip[pripl] == '\0' || prip[pripl] == '/')) + return mntlen; /* strip all */ + } + /* + * Note that it is not sufficient to check for the + * first 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_ @@ -631,6 +719,8 @@ register struct mount *mp; register struct statfs *sp; int error; + int pripl; + int nonsu; struct nameidata nd; struct statfs sb; @@ -641,13 +731,19 @@ sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); + if ((pripl = check_prison_mount(p, sp, 1)) < 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))); @@ -675,6 +771,8 @@ 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) @@ -683,13 +781,19 @@ if (mp == NULL) return (EBADF); sp = &mp->mnt_stat; + if ((pripl = check_prison_mount(p, sp, 1)) < 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))); @@ -718,13 +822,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, 0); + if (pripl < 0 || vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } @@ -744,6 +851,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); --- src/sys/sys/jail.h.orig Wed Nov 1 18:58:06 2000 +++ src/sys/sys/jail.h Wed Feb 27 15:40:17 2002 @@ -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; };