mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
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
This commit is contained in:
parent
db5f2ca8df
commit
fd7a8150fb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=113275
@ -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
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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;
|
||||
mtx_lock(&Giant);
|
||||
error = chroot(td, &ca);
|
||||
mtx_unlock(&Giant);
|
||||
if (error)
|
||||
goto bail;
|
||||
newcred = crget();
|
||||
goto e_dropvnref;
|
||||
pr->pr_ip = j.ip_number;
|
||||
PROC_LOCK(p);
|
||||
/* Implicitly fail if already in jail. */
|
||||
error = suser_cred(p->p_ucred, 0);
|
||||
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 badcred;
|
||||
oldcred = p->p_ucred;
|
||||
crcopy(newcred, oldcred);
|
||||
p->p_ucred = newcred;
|
||||
p->p_ucred->cr_prison = pr;
|
||||
pr->pr_ref = 1;
|
||||
PROC_UNLOCK(p);
|
||||
crfree(oldcred);
|
||||
goto e_dropprref;
|
||||
mtx_lock(&pr->pr_mtx);
|
||||
pr->pr_ref--;
|
||||
mtx_unlock(&pr->pr_mtx);
|
||||
td->td_retval[0] = jaa.jid;
|
||||
return (0);
|
||||
badcred:
|
||||
PROC_UNLOCK(p);
|
||||
crfree(newcred);
|
||||
bail:
|
||||
e_dropprref:
|
||||
mtx_lock(&allprison_mtx);
|
||||
LIST_REMOVE(pr, pr_list);
|
||||
prisoncount--;
|
||||
mtx_unlock(&allprison_mtx);
|
||||
e_dropvnref:
|
||||
mtx_lock(&Giant);
|
||||
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 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();
|
||||
PROC_LOCK(p);
|
||||
/* Implicitly fail if already in jail. */
|
||||
error = suser_cred(p->p_ucred, 0);
|
||||
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;
|
||||
mtx_unlock(&pr->pr_mtx);
|
||||
PROC_UNLOCK(p);
|
||||
crfree(oldcred);
|
||||
return (0);
|
||||
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");
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,32 +566,63 @@ chroot(td, uap)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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,32 +566,63 @@ chroot(td, uap)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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().
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user