freebsd-dev/sys/kern/kern_jail.c
John Baldwin a2f2b3afcd - Catch up to the new ucred API.
- Add proc locking to the jail() syscall.  This mostly involved shuffling
  a few things around so that blockable things like malloc and copyin
  were performed before acquiring the lock and checking the existing
  ucred and then updating the ucred as one "atomic" change under the proc
  lock.
2001-10-11 23:39:43 +00:00

222 lines
4.4 KiB
C

/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
* $FreeBSD$
*
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/sysproto.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/jail.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <netinet/in.h>
MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
SYSCTL_NODE(, OID_AUTO, jail, CTLFLAG_RW, 0,
"Jail rules");
mp_fixme("these variables need a lock")
int jail_set_hostname_allowed = 1;
SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
&jail_set_hostname_allowed, 0,
"Processes in jail can set their hostnames");
int jail_socket_unixiproute_only = 1;
SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
&jail_socket_unixiproute_only, 0,
"Processes in jail are limited to creating UNIX/IPv4/route sockets only");
int jail_sysvipc_allowed = 0;
SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
&jail_sysvipc_allowed, 0,
"Processes in jail can use System V IPC primitives");
/*
* MPSAFE
*/
int
jail(td, uap)
struct thread *td;
struct jail_args /* {
syscallarg(struct jail *) jail;
} */ *uap;
{
struct proc *p = td->td_proc;
int error;
struct prison *pr;
struct jail j;
struct chroot_args ca;
struct ucred *newcred = NULL, *oldcred;
error = copyin(uap->jail, &j, sizeof j);
if (error)
return (error);
if (j.version != 0)
return (EINVAL);
mtx_lock(&Giant);
MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
pr->pr_securelevel = securelevel;
error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
if (error)
goto bail;
ca.path = j.path;
error = chroot(td, &ca);
if (error)
goto bail;
newcred = crget();
pr->pr_ip = j.ip_number;
PROC_LOCK(p);
/* Implicitly fail if already in jail. */
error = suser(p);
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);
mtx_unlock(&Giant);
return (0);
badcred:
PROC_UNLOCK(p);
crfree(newcred);
bail:
FREE(pr, M_PRISON);
mtx_unlock(&Giant);
return (error);
}
void
prison_free(struct prison *pr)
{
pr->pr_ref--;
if (pr->pr_ref == 0) {
if (pr->pr_linux != NULL)
FREE(pr->pr_linux, M_PRISON);
FREE(pr, M_PRISON);
}
}
void
prison_hold(struct prison *pr)
{
pr->pr_ref++;
}
int
prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
{
u_int32_t tmp;
if (!jailed(cred))
return (0);
if (flag)
tmp = *ip;
else
tmp = ntohl(*ip);
if (tmp == INADDR_ANY) {
if (flag)
*ip = cred->cr_prison->pr_ip;
else
*ip = htonl(cred->cr_prison->pr_ip);
return (0);
}
if (tmp == INADDR_LOOPBACK) {
if (flag)
*ip = cred->cr_prison->pr_ip;
else
*ip = htonl(cred->cr_prison->pr_ip);
return (0);
}
if (cred->cr_prison->pr_ip != tmp)
return (1);
return (0);
}
void
prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip)
{
u_int32_t tmp;
if (!jailed(cred))
return;
if (flag)
tmp = *ip;
else
tmp = ntohl(*ip);
if (tmp == INADDR_LOOPBACK) {
if (flag)
*ip = cred->cr_prison->pr_ip;
else
*ip = htonl(cred->cr_prison->pr_ip);
return;
}
return;
}
int
prison_if(struct ucred *cred, struct sockaddr *sa)
{
struct sockaddr_in *sai = (struct sockaddr_in*) sa;
int ok;
if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
ok = 1;
else if (sai->sin_family != AF_INET)
ok = 0;
else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr))
ok = 1;
else
ok = 0;
return (ok);
}
/*
* Return 0 if jails permit p1 to frob p2, otherwise ESRCH.
*/
int
prison_check(cred1, cred2)
struct ucred *cred1, *cred2;
{
if (jailed(cred1)) {
if (!jailed(cred2))
return (ESRCH);
if (cred2->cr_prison != cred1->cr_prison)
return (ESRCH);
}
return (0);
}
/*
* Return 1 if the passed credential is in a jail, otherwise 0.
*/
int
jailed(cred)
struct ucred *cred;
{
return (cred->cr_prison != NULL);
}