diff options
author | mike <mike@FreeBSD.org> | 2003-04-09 02:55:18 +0000 |
---|---|---|
committer | mike <mike@FreeBSD.org> | 2003-04-09 02:55:18 +0000 |
commit | 75859ca578ff1bc109e1263e5c52d225315515e0 (patch) | |
tree | c6122edf636b885d1df318cda6d94636af3212f8 | |
parent | 979ed3a82ea34d46466c4d6f755b15b85df66f15 (diff) | |
download | FreeBSD-src-75859ca578ff1bc109e1263e5c52d225315515e0.zip FreeBSD-src-75859ca578ff1bc109e1263e5c52d225315515e0.tar.gz |
o In struct prison, add an allprison linked list of prisons (protected
by allprison_mtx), a unique prison/jail identifier field, two path
fields (pr_path for reporting and pr_root vnode instance) to store
the chroot() point of each jail.
o Add jail_attach(2) to allow a process to bind to an existing jail.
o Add change_root() to perform the chroot operation on a specified
vnode.
o Generalize change_dir() to accept a vnode, and move namei() calls
to callers of change_dir().
o Add a new sysctl (security.jail.list) which is a group of
struct xprison instances that represent a snapshot of active jails.
Reviewed by: rwatson, tjr
-rw-r--r-- | lib/libc/sys/Makefile.inc | 1 | ||||
-rw-r--r-- | lib/libc/sys/jail.2 | 32 | ||||
-rw-r--r-- | sys/amd64/ia32/syscalls.master | 1 | ||||
-rw-r--r-- | sys/compat/freebsd32/syscalls.master | 1 | ||||
-rw-r--r-- | sys/ia64/ia32/syscalls.master | 1 | ||||
-rw-r--r-- | sys/kern/kern_jail.c | 238 | ||||
-rw-r--r-- | sys/kern/subr_witness.c | 1 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 1 | ||||
-rw-r--r-- | sys/kern/vfs_extattr.c | 109 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 109 | ||||
-rw-r--r-- | sys/sys/jail.h | 20 |
11 files changed, 403 insertions, 111 deletions
diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 122d4ae..7c070e1 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -115,6 +115,7 @@ MLINKS+=getsockopt.2 setsockopt.2 MLINKS+=gettimeofday.2 settimeofday.2 MLINKS+=getuid.2 geteuid.2 MLINKS+=intro.2 errno.2 +MLINKS+=jail.2 jail_attach.2 MLINKS+=kqueue.2 kevent.2 MLINKS+=kse.2 kse_create.2 kse.2 kse_exit.2 kse.2 kse_release.2 \ kse.2 kse_wakeup.2 kse.2 kse_thr_interrupt.2 diff --git a/lib/libc/sys/jail.2 b/lib/libc/sys/jail.2 index 4656438..288f30d 100644 --- a/lib/libc/sys/jail.2 +++ b/lib/libc/sys/jail.2 @@ -8,7 +8,7 @@ .\" .\"$FreeBSD$ .\" -.Dd April 28, 1999 +.Dd April 8, 2003 .Dt JAIL 2 .Os .Sh NAME @@ -17,10 +17,12 @@ .Sh LIBRARY .Lb libc .Sh SYNOPSIS -.In sys/types.h +.In sys/param.h .In sys/jail.h .Ft int .Fn jail "struct jail *jail" +.Ft int +.Fn jail_attach "int jid" .Sh DESCRIPTION The .Fn jail @@ -52,9 +54,29 @@ from the inside of the prison. The .Dq Li ip_number can be set to the IP number assigned to the prison. +.Pp +The +.Fn jail_attach +system call attaches the current process to an existing jail, +identified by +.Va jid . +.Sh RETURN VALUES +If successful, +.Fn jail +returns a non-negative integer, termed the jail identifier (JID). +It returns -1 on failure, and sets +.Va errno +to indicate the error. +.Pp +If successful, +.Fn jail_attach +returns 0. +It returns -1 on failure, and sets +.Va errno +to indicate the error. .Sh PRISON? Once a process has been put in a prison, it and its decendants cannot escape -the prison. It is not possible to add a process to a preexisting prison. +the prison. .Pp Inside the prison, the concept of "superuser" is very diluted. In general, it can be assumed that nothing can be mangled from inside a prison which @@ -100,6 +122,10 @@ The .Fn jail system call appeared in .Fx 4.0 . +The +.Fn jail_attach +system call appeared in +.Fx 5.1 . .Sh AUTHORS The jail feature was written by .An Poul-Henning Kamp diff --git a/sys/amd64/ia32/syscalls.master b/sys/amd64/ia32/syscalls.master index 48ce6cf..e462377 100644 --- a/sys/amd64/ia32/syscalls.master +++ b/sys/amd64/ia32/syscalls.master @@ -607,4 +607,5 @@ 433 STD BSD { int thr_kill(thr_id_t id, int sig); } 434 MSTD BSD { int _umtx_lock(struct umtx *umtx); } 435 MSTD BSD { int _umtx_unlock(struct umtx *umtx); } +436 MSTD BSD { int jail_attach(int jid); } diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 48ce6cf..e462377 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -607,4 +607,5 @@ 433 STD BSD { int thr_kill(thr_id_t id, int sig); } 434 MSTD BSD { int _umtx_lock(struct umtx *umtx); } 435 MSTD BSD { int _umtx_unlock(struct umtx *umtx); } +436 MSTD BSD { int jail_attach(int jid); } diff --git a/sys/ia64/ia32/syscalls.master b/sys/ia64/ia32/syscalls.master index 48ce6cf..e462377 100644 --- a/sys/ia64/ia32/syscalls.master +++ b/sys/ia64/ia32/syscalls.master @@ -607,4 +607,5 @@ 433 STD BSD { int thr_kill(thr_id_t id, int sig); } 434 MSTD BSD { int _umtx_lock(struct umtx *umtx); } 435 MSTD BSD { int _umtx_unlock(struct umtx *umtx); } +436 MSTD BSD { int jail_attach(int jid); } diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 15f4b36..c4b1c94 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -21,8 +21,12 @@ #include <sys/jail.h> #include <sys/lock.h> #include <sys/mutex.h> +#include <sys/namei.h> +#include <sys/queue.h> #include <sys/socket.h> +#include <sys/syscallsubr.h> #include <sys/sysctl.h> +#include <sys/vnode.h> #include <net/if.h> #include <netinet/in.h> @@ -49,6 +53,26 @@ SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW, &jail_sysvipc_allowed, 0, "Processes in jail can use System V IPC primitives"); +/* allprison, lastprid, and prisoncount are protected by allprison_mtx. */ +struct prisonlist allprison; +struct mtx allprison_mtx; +int lastprid = 0; +int prisoncount = 0; + +static void init_prison(void *); +static struct prison *prison_find(int); +static int sysctl_jail_list(SYSCTL_HANDLER_ARGS); + +static void +init_prison(void *data __unused) +{ + + mtx_init(&allprison_mtx, "allprison", NULL, MTX_DEF); + LIST_INIT(&allprison); +} + +SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL); + /* * MPSAFE */ @@ -59,12 +83,11 @@ jail(td, uap) struct jail *jail; } */ *uap; { - struct proc *p = td->td_proc; - int error; - struct prison *pr; + struct nameidata nd; + struct prison *pr, *tpr; struct jail j; - struct chroot_args ca; - struct ucred *newcred = NULL, *oldcred; + struct jail_attach_args jaa; + int error, tryprid; error = copyin(uap->jail, &j, sizeof j); if (error) @@ -74,48 +97,176 @@ jail(td, uap) MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO); mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF); - pr->pr_securelevel = securelevel; + pr->pr_ref = 1; + error = copyinstr(j.path, &pr->pr_path, sizeof pr->pr_path, 0); + if (error) + goto e_killmtx; + mtx_lock(&Giant); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, pr->pr_path, td); + error = namei(&nd); + if (error) { + mtx_unlock(&Giant); + goto e_killmtx; + } + pr->pr_root = nd.ni_vp; + VOP_UNLOCK(nd.ni_vp, 0, td); + NDFREE(&nd, NDF_ONLY_PNBUF); + mtx_unlock(&Giant); error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0); if (error) - goto bail; - ca.path = j.path; + goto e_dropvnref; + pr->pr_ip = j.ip_number; + pr->pr_linux = NULL; + pr->pr_securelevel = securelevel; + + /* Determine next pr_id and add prison to allprison list. */ + mtx_lock(&allprison_mtx); + tryprid = lastprid + 1; + if (tryprid == JAIL_MAX) + tryprid = 1; +next: + LIST_FOREACH(tpr, &allprison, pr_list) { + if (tpr->pr_id == tryprid) { + tryprid++; + if (tryprid == JAIL_MAX) { + mtx_unlock(&allprison_mtx); + error = EAGAIN; + goto e_dropvnref; + } + goto next; + } + } + pr->pr_id = jaa.jid = lastprid = tryprid; + LIST_INSERT_HEAD(&allprison, pr, pr_list); + prisoncount++; + mtx_unlock(&allprison_mtx); + + error = jail_attach(td, &jaa); + if (error) + goto e_dropprref; + mtx_lock(&pr->pr_mtx); + pr->pr_ref--; + mtx_unlock(&pr->pr_mtx); + td->td_retval[0] = jaa.jid; + return (0); +e_dropprref: + mtx_lock(&allprison_mtx); + LIST_REMOVE(pr, pr_list); + prisoncount--; + mtx_unlock(&allprison_mtx); +e_dropvnref: mtx_lock(&Giant); - error = chroot(td, &ca); + vrele(pr->pr_root); mtx_unlock(&Giant); +e_killmtx: + mtx_destroy(&pr->pr_mtx); + FREE(pr, M_PRISON); + return (error); +} + +/* + * MPSAFE + */ +int +jail_attach(td, uap) + struct thread *td; + struct jail_attach_args /* { + int jid; + } */ *uap; +{ + struct proc *p; + struct ucred *newcred, *oldcred; + struct prison *pr; + int error; + + p = td->td_proc; + + mtx_lock(&allprison_mtx); + pr = prison_find(uap->jid); + if (pr == NULL) { + mtx_unlock(&allprison_mtx); + return (EINVAL); + } + pr->pr_ref++; + mtx_unlock(&pr->pr_mtx); + mtx_unlock(&allprison_mtx); + + error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) - goto bail; + goto e_dropref; + mtx_lock(&Giant); + vn_lock(pr->pr_root, LK_EXCLUSIVE | LK_RETRY, td); + if ((error = change_dir(pr->pr_root, td)) != 0) + goto e_unlock; +#ifdef MAC + if ((error = mac_check_vnode_chroot(td->td_ucred, pr->pr_root))) + goto e_unlock; +#endif + VOP_UNLOCK(pr->pr_root, 0, td); + change_root(pr->pr_root, td); + mtx_unlock(&Giant); + newcred = crget(); - pr->pr_ip = j.ip_number; PROC_LOCK(p); /* Implicitly fail if already in jail. */ error = suser_cred(p->p_ucred, 0); - if (error) - goto badcred; + if (error) { + PROC_UNLOCK(p); + crfree(newcred); + goto e_dropref; + } oldcred = p->p_ucred; + setsugid(p); crcopy(newcred, oldcred); p->p_ucred = newcred; + mtx_lock(&pr->pr_mtx); p->p_ucred->cr_prison = pr; - pr->pr_ref = 1; + mtx_unlock(&pr->pr_mtx); PROC_UNLOCK(p); crfree(oldcred); return (0); -badcred: - PROC_UNLOCK(p); - crfree(newcred); -bail: - mtx_destroy(&pr->pr_mtx); - FREE(pr, M_PRISON); +e_unlock: + VOP_UNLOCK(pr->pr_root, 0, td); + mtx_unlock(&Giant); +e_dropref: + mtx_lock(&pr->pr_mtx); + pr->pr_ref--; + mtx_unlock(&pr->pr_mtx); return (error); } +/* + * Returns a locked prison instance, or NULL on failure. + */ +static struct prison * +prison_find(int prid) +{ + struct prison *pr; + + mtx_assert(&allprison_mtx, MA_OWNED); + LIST_FOREACH(pr, &allprison, pr_list) { + if (pr->pr_id == prid) { + mtx_lock(&pr->pr_mtx); + return (pr); + } + } + return (NULL); +} + void prison_free(struct prison *pr) { + mtx_assert(&Giant, MA_OWNED); + mtx_lock(&allprison_mtx); mtx_lock(&pr->pr_mtx); pr->pr_ref--; if (pr->pr_ref == 0) { + LIST_REMOVE(pr, pr_list); mtx_unlock(&pr->pr_mtx); + prisoncount--; + mtx_unlock(&allprison_mtx); + vrele(pr->pr_root); mtx_destroy(&pr->pr_mtx); if (pr->pr_linux != NULL) FREE(pr->pr_linux, M_PRISON); @@ -123,6 +274,7 @@ prison_free(struct prison *pr) return; } mtx_unlock(&pr->pr_mtx); + mtx_unlock(&allprison_mtx); } void @@ -256,3 +408,49 @@ getcredhostname(cred, buf, size) else strlcpy(buf, hostname, size); } + +static int +sysctl_jail_list(SYSCTL_HANDLER_ARGS) +{ + struct xprison *xp, *sxp; + struct prison *pr; + int count, error; + + mtx_assert(&Giant, MA_OWNED); +retry: + mtx_lock(&allprison_mtx); + count = prisoncount; + mtx_unlock(&allprison_mtx); + + if (count == 0) + return (0); + + sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); + mtx_lock(&allprison_mtx); + if (count != prisoncount) { + mtx_unlock(&allprison_mtx); + free(sxp, M_TEMP); + goto retry; + } + + LIST_FOREACH(pr, &allprison, pr_list) { + mtx_lock(&pr->pr_mtx); + xp->pr_version = XPRISON_VERSION; + xp->pr_id = pr->pr_id; + strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path)); + strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host)); + xp->pr_ip = pr->pr_ip; + mtx_unlock(&pr->pr_mtx); + xp++; + } + mtx_unlock(&allprison_mtx); + + error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count); + free(sxp, M_TEMP); + if (error) + return (error); + return (0); +} + +SYSCTL_OID(_security_jail, OID_AUTO, list, CTLTYPE_STRUCT | CTLFLAG_RD, + NULL, 0, sysctl_jail_list, "S", "List of active jails"); diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c index 3b7526a..d89698b 100644 --- a/sys/kern/subr_witness.c +++ b/sys/kern/subr_witness.c @@ -266,6 +266,7 @@ static struct witness_order_list_entry order_lists[] = { { "session", &lock_class_mtx_sleep }, { "uidinfo hash", &lock_class_mtx_sleep }, { "uidinfo struct", &lock_class_mtx_sleep }, + { "allprison", &lock_class_mtx_sleep }, { NULL, NULL }, /* * spin locks diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index b5c729b..cfb84f0 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -630,6 +630,7 @@ 433 MSTD BSD { int thr_kill(thr_id_t id, int sig); } 434 MSTD BSD { int _umtx_lock(struct umtx *umtx); } 435 MSTD BSD { int _umtx_unlock(struct umtx *umtx); } +436 MSTD BSD { int jail_attach(int jid); } ; Please copy any additions and changes to the following compatability tables: ; sys/ia64/ia32/syscalls.master (take a best guess) diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c index 6e8656b..45ba5a1 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -78,7 +78,6 @@ #include <vm/vm_page.h> #include <vm/uma.h> -static int change_dir(struct nameidata *ndp, struct thread *td); static int chroot_refuse_vdir_fds(struct filedesc *fdp); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); static int setfown(struct thread *td, struct vnode *, uid_t, gid_t); @@ -463,8 +462,13 @@ kern_chdir(struct thread *td, char *path, enum uio_seg pathseg) struct vnode *vp; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pathseg, path, td); - if ((error = change_dir(&nd, td)) != 0) + if ((error = namei(&nd)) != 0) return (error); + if ((error = change_dir(nd.ni_vp, td)) != 0) { + vput(nd.ni_vp); + NDFREE(&nd, NDF_ONLY_PNBUF); + return (error); + } VOP_UNLOCK(nd.ni_vp, 0, td); NDFREE(&nd, NDF_ONLY_PNBUF); FILEDESC_LOCK(fdp); @@ -530,45 +534,31 @@ chroot(td, uap) char *path; } */ *uap; { - register struct filedesc *fdp = td->td_proc->p_fd; int error; struct nameidata nd; - struct vnode *vp; error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td); mtx_lock(&Giant); - if ((error = change_dir(&nd, td)) != 0) + error = namei(&nd); + if (error) goto error; + if ((error = change_dir(nd.ni_vp, td)) != 0) + goto e_vunlock; #ifdef MAC - if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) { - vput(nd.ni_vp); - goto error; - } + if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) + goto e_vunlock; #endif VOP_UNLOCK(nd.ni_vp, 0, td); - FILEDESC_LOCK(fdp); - if (chroot_allow_open_directories == 0 || - (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { - error = chroot_refuse_vdir_fds(fdp); - if (error) - goto error_unlock; - } - vp = fdp->fd_rdir; - fdp->fd_rdir = nd.ni_vp; - if (!fdp->fd_jdir) { - fdp->fd_jdir = nd.ni_vp; - VREF(fdp->fd_jdir); - } - FILEDESC_UNLOCK(fdp); + error = change_root(nd.ni_vp, td); + vrele(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); - vrele(vp); mtx_unlock(&Giant); - return (0); -error_unlock: - FILEDESC_UNLOCK(fdp); + return (error); +e_vunlock: + vput(nd.ni_vp); error: mtx_unlock(&Giant); NDFREE(&nd, NDF_ONLY_PNBUF); @@ -576,35 +566,66 @@ error: } /* - * Common routine for chroot and chdir. On success, the directory vnode - * is returned locked, and must be unlocked by the caller. + * Common routine for chroot and chdir. Callers must provide a locked vnode + * instance. */ -static int -change_dir(ndp, td) - register struct nameidata *ndp; +int +change_dir(vp, td) + struct vnode *vp; struct thread *td; { - struct vnode *vp; int error; - error = namei(ndp); - if (error) - return (error); - vp = ndp->ni_vp; + ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); if (vp->v_type != VDIR) - error = ENOTDIR; + return (ENOTDIR); #ifdef MAC - if (error == 0) - error = mac_check_vnode_chdir(td->td_ucred, vp); -#endif - if (error == 0) - error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); + error = mac_check_vnode_chdir(td->td_ucred, vp); if (error) - vput(vp); + return (error); +#endif + error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); return (error); } /* + * Common routine for kern_chroot() and jail_attach(). The caller is + * responsible for invoking suser() and mac_check_chroot() to authorize this + * operation. + */ +int +change_root(vp, td) + struct vnode *vp; + struct thread *td; +{ + struct filedesc *fdp; + struct vnode *oldvp; + int error; + + mtx_assert(&Giant, MA_OWNED); + fdp = td->td_proc->p_fd; + FILEDESC_LOCK(fdp); + if (chroot_allow_open_directories == 0 || + (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { + error = chroot_refuse_vdir_fds(fdp); + if (error) { + FILEDESC_UNLOCK(fdp); + return (error); + } + } + oldvp = fdp->fd_rdir; + fdp->fd_rdir = vp; + VREF(fdp->fd_rdir); + if (!fdp->fd_jdir) { + fdp->fd_jdir = vp; + VREF(fdp->fd_jdir); + } + FILEDESC_UNLOCK(fdp); + vrele(oldvp); + return (0); +} + +/* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 6e8656b..45ba5a1 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -78,7 +78,6 @@ #include <vm/vm_page.h> #include <vm/uma.h> -static int change_dir(struct nameidata *ndp, struct thread *td); static int chroot_refuse_vdir_fds(struct filedesc *fdp); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); static int setfown(struct thread *td, struct vnode *, uid_t, gid_t); @@ -463,8 +462,13 @@ kern_chdir(struct thread *td, char *path, enum uio_seg pathseg) struct vnode *vp; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pathseg, path, td); - if ((error = change_dir(&nd, td)) != 0) + if ((error = namei(&nd)) != 0) return (error); + if ((error = change_dir(nd.ni_vp, td)) != 0) { + vput(nd.ni_vp); + NDFREE(&nd, NDF_ONLY_PNBUF); + return (error); + } VOP_UNLOCK(nd.ni_vp, 0, td); NDFREE(&nd, NDF_ONLY_PNBUF); FILEDESC_LOCK(fdp); @@ -530,45 +534,31 @@ chroot(td, uap) char *path; } */ *uap; { - register struct filedesc *fdp = td->td_proc->p_fd; int error; struct nameidata nd; - struct vnode *vp; error = suser_cred(td->td_ucred, PRISON_ROOT); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td); mtx_lock(&Giant); - if ((error = change_dir(&nd, td)) != 0) + error = namei(&nd); + if (error) goto error; + if ((error = change_dir(nd.ni_vp, td)) != 0) + goto e_vunlock; #ifdef MAC - if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) { - vput(nd.ni_vp); - goto error; - } + if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) + goto e_vunlock; #endif VOP_UNLOCK(nd.ni_vp, 0, td); - FILEDESC_LOCK(fdp); - if (chroot_allow_open_directories == 0 || - (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { - error = chroot_refuse_vdir_fds(fdp); - if (error) - goto error_unlock; - } - vp = fdp->fd_rdir; - fdp->fd_rdir = nd.ni_vp; - if (!fdp->fd_jdir) { - fdp->fd_jdir = nd.ni_vp; - VREF(fdp->fd_jdir); - } - FILEDESC_UNLOCK(fdp); + error = change_root(nd.ni_vp, td); + vrele(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); - vrele(vp); mtx_unlock(&Giant); - return (0); -error_unlock: - FILEDESC_UNLOCK(fdp); + return (error); +e_vunlock: + vput(nd.ni_vp); error: mtx_unlock(&Giant); NDFREE(&nd, NDF_ONLY_PNBUF); @@ -576,35 +566,66 @@ error: } /* - * Common routine for chroot and chdir. On success, the directory vnode - * is returned locked, and must be unlocked by the caller. + * Common routine for chroot and chdir. Callers must provide a locked vnode + * instance. */ -static int -change_dir(ndp, td) - register struct nameidata *ndp; +int +change_dir(vp, td) + struct vnode *vp; struct thread *td; { - struct vnode *vp; int error; - error = namei(ndp); - if (error) - return (error); - vp = ndp->ni_vp; + ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); if (vp->v_type != VDIR) - error = ENOTDIR; + return (ENOTDIR); #ifdef MAC - if (error == 0) - error = mac_check_vnode_chdir(td->td_ucred, vp); -#endif - if (error == 0) - error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); + error = mac_check_vnode_chdir(td->td_ucred, vp); if (error) - vput(vp); + return (error); +#endif + error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); return (error); } /* + * Common routine for kern_chroot() and jail_attach(). The caller is + * responsible for invoking suser() and mac_check_chroot() to authorize this + * operation. + */ +int +change_root(vp, td) + struct vnode *vp; + struct thread *td; +{ + struct filedesc *fdp; + struct vnode *oldvp; + int error; + + mtx_assert(&Giant, MA_OWNED); + fdp = td->td_proc->p_fd; + FILEDESC_LOCK(fdp); + if (chroot_allow_open_directories == 0 || + (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { + error = chroot_refuse_vdir_fds(fdp); + if (error) { + FILEDESC_UNLOCK(fdp); + return (error); + } + } + oldvp = fdp->fd_rdir; + fdp->fd_rdir = vp; + VREF(fdp->fd_rdir); + if (!fdp->fd_jdir) { + fdp->fd_jdir = vp; + VREF(fdp->fd_jdir); + } + FILEDESC_UNLOCK(fdp); + vrele(oldvp); + return (0); +} + +/* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ diff --git a/sys/sys/jail.h b/sys/sys/jail.h index 016c75a..fbc8ba93 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -20,9 +20,19 @@ struct jail { u_int32_t ip_number; }; +struct xprison { + int pr_version; + int pr_id; + char pr_path[MAXPATHLEN]; + char pr_host[MAXHOSTNAMELEN]; + u_int32_t pr_ip; +}; +#define XPRISON_VERSION 1 + #ifndef _KERNEL int jail(struct jail *); +int jail_attach(int); #else /* _KERNEL */ @@ -30,6 +40,8 @@ int jail(struct jail *); #include <sys/_lock.h> #include <sys/_mutex.h> +#define JAIL_MAX 999999 + #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_PRISON); #endif @@ -40,13 +52,18 @@ MALLOC_DECLARE(M_PRISON); * delete the struture when the last inmate is dead. * * Lock key: + * (a) allprison_mutex * (p) locked by pr_mutex * (c) set only during creation before the structure is shared, no mutex * required to read */ struct mtx; struct prison { + LIST_ENTRY(prison) pr_list; /* (a) all prisons */ + int pr_id; /* (c) prison id */ int pr_ref; /* (p) refcount */ + char pr_path[MAXPATHLEN]; /* (c) chroot path */ + struct vnode *pr_root; /* (c) vnode to rdir */ char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */ u_int32_t pr_ip; /* (c) ip addr host */ void *pr_linux; /* (p) linux abi */ @@ -63,6 +80,9 @@ extern int jail_set_hostname_allowed; extern int jail_socket_unixiproute_only; extern int jail_sysvipc_allowed; +LIST_HEAD(prisonlist, prison); +extern struct prisonlist allprison; + /* * Kernel support functions for jail(). */ |