kern_jail.c revision 113630
146197Sphk/* 246197Sphk * ---------------------------------------------------------------------------- 346197Sphk * "THE BEER-WARE LICENSE" (Revision 42): 446197Sphk * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 546197Sphk * can do whatever you want with this stuff. If we meet some day, and you think 646197Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 746197Sphk * ---------------------------------------------------------------------------- 846197Sphk * 950477Speter * $FreeBSD: head/sys/kern/kern_jail.c 113630 2003-04-17 22:26:53Z jhb $ 1046197Sphk * 1146197Sphk */ 1246155Sphk 1346155Sphk#include <sys/param.h> 1446155Sphk#include <sys/types.h> 1546155Sphk#include <sys/kernel.h> 1646155Sphk#include <sys/systm.h> 1746155Sphk#include <sys/errno.h> 1846155Sphk#include <sys/sysproto.h> 1946155Sphk#include <sys/malloc.h> 2046155Sphk#include <sys/proc.h> 2146155Sphk#include <sys/jail.h> 2287275Srwatson#include <sys/lock.h> 2387275Srwatson#include <sys/mutex.h> 24113275Smike#include <sys/namei.h> 25113275Smike#include <sys/queue.h> 2646155Sphk#include <sys/socket.h> 27113275Smike#include <sys/syscallsubr.h> 2857163Srwatson#include <sys/sysctl.h> 29113275Smike#include <sys/vnode.h> 3046155Sphk#include <net/if.h> 3146155Sphk#include <netinet/in.h> 3246155Sphk 3346155SphkMALLOC_DEFINE(M_PRISON, "prison", "Prison structures"); 3446155Sphk 3589414SarrSYSCTL_DECL(_security); 3689414SarrSYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW, 0, 3757163Srwatson "Jail rules"); 3857163Srwatson 3984828Sjhbmp_fixme("these variables need a lock") 4084828Sjhb 4157163Srwatsonint jail_set_hostname_allowed = 1; 4289414SarrSYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW, 4357163Srwatson &jail_set_hostname_allowed, 0, 4457163Srwatson "Processes in jail can set their hostnames"); 4557163Srwatson 4661235Srwatsonint jail_socket_unixiproute_only = 1; 4789414SarrSYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW, 4861235Srwatson &jail_socket_unixiproute_only, 0, 4961235Srwatson "Processes in jail are limited to creating UNIX/IPv4/route sockets only"); 5061235Srwatson 5168024Srwatsonint jail_sysvipc_allowed = 0; 5289414SarrSYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW, 5368024Srwatson &jail_sysvipc_allowed, 0, 5468024Srwatson "Processes in jail can use System V IPC primitives"); 5568024Srwatson 56113275Smike/* allprison, lastprid, and prisoncount are protected by allprison_mtx. */ 57113275Smikestruct prisonlist allprison; 58113275Smikestruct mtx allprison_mtx; 59113275Smikeint lastprid = 0; 60113275Smikeint prisoncount = 0; 61113275Smike 62113275Smikestatic void init_prison(void *); 63113275Smikestatic struct prison *prison_find(int); 64113275Smikestatic int sysctl_jail_list(SYSCTL_HANDLER_ARGS); 65113275Smike 66113275Smikestatic void 67113275Smikeinit_prison(void *data __unused) 68113275Smike{ 69113275Smike 70113275Smike mtx_init(&allprison_mtx, "allprison", NULL, MTX_DEF); 71113275Smike LIST_INIT(&allprison); 72113275Smike} 73113275Smike 74113275SmikeSYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL); 75113275Smike 7682710Sdillon/* 7782710Sdillon * MPSAFE 7882710Sdillon */ 7946155Sphkint 8083366Sjulianjail(td, uap) 8183366Sjulian struct thread *td; 8272786Srwatson struct jail_args /* { 83107850Salfred struct jail *jail; 8472786Srwatson } */ *uap; 8546155Sphk{ 86113275Smike struct nameidata nd; 87113275Smike struct prison *pr, *tpr; 8846155Sphk struct jail j; 89113275Smike struct jail_attach_args jaa; 90113275Smike int error, tryprid; 9146155Sphk 9246155Sphk error = copyin(uap->jail, &j, sizeof j); 9346155Sphk if (error) 9484828Sjhb return (error); 9584828Sjhb if (j.version != 0) 9684828Sjhb return (EINVAL); 9784828Sjhb 98111119Simp MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO); 9993818Sjhb mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF); 100113275Smike pr->pr_ref = 1; 101113275Smike error = copyinstr(j.path, &pr->pr_path, sizeof pr->pr_path, 0); 102113275Smike if (error) 103113275Smike goto e_killmtx; 104113275Smike mtx_lock(&Giant); 105113275Smike NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, pr->pr_path, td); 106113275Smike error = namei(&nd); 107113275Smike if (error) { 108113275Smike mtx_unlock(&Giant); 109113275Smike goto e_killmtx; 110113275Smike } 111113275Smike pr->pr_root = nd.ni_vp; 112113275Smike VOP_UNLOCK(nd.ni_vp, 0, td); 113113275Smike NDFREE(&nd, NDF_ONLY_PNBUF); 114113275Smike mtx_unlock(&Giant); 11546155Sphk error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0); 11684828Sjhb if (error) 117113275Smike goto e_dropvnref; 118113275Smike pr->pr_ip = j.ip_number; 119113275Smike pr->pr_linux = NULL; 120113275Smike pr->pr_securelevel = securelevel; 121113275Smike 122113275Smike /* Determine next pr_id and add prison to allprison list. */ 123113275Smike mtx_lock(&allprison_mtx); 124113275Smike tryprid = lastprid + 1; 125113275Smike if (tryprid == JAIL_MAX) 126113275Smike tryprid = 1; 127113275Smikenext: 128113275Smike LIST_FOREACH(tpr, &allprison, pr_list) { 129113275Smike if (tpr->pr_id == tryprid) { 130113275Smike tryprid++; 131113275Smike if (tryprid == JAIL_MAX) { 132113275Smike mtx_unlock(&allprison_mtx); 133113275Smike error = EAGAIN; 134113275Smike goto e_dropvnref; 135113275Smike } 136113275Smike goto next; 137113275Smike } 138113275Smike } 139113275Smike pr->pr_id = jaa.jid = lastprid = tryprid; 140113275Smike LIST_INSERT_HEAD(&allprison, pr, pr_list); 141113275Smike prisoncount++; 142113275Smike mtx_unlock(&allprison_mtx); 143113275Smike 144113275Smike error = jail_attach(td, &jaa); 145113275Smike if (error) 146113275Smike goto e_dropprref; 147113275Smike mtx_lock(&pr->pr_mtx); 148113275Smike pr->pr_ref--; 149113275Smike mtx_unlock(&pr->pr_mtx); 150113275Smike td->td_retval[0] = jaa.jid; 151113275Smike return (0); 152113275Smikee_dropprref: 153113275Smike mtx_lock(&allprison_mtx); 154113275Smike LIST_REMOVE(pr, pr_list); 155113275Smike prisoncount--; 156113275Smike mtx_unlock(&allprison_mtx); 157113275Smikee_dropvnref: 15899227Siedowse mtx_lock(&Giant); 159113275Smike vrele(pr->pr_root); 16099227Siedowse mtx_unlock(&Giant); 161113275Smikee_killmtx: 162113275Smike mtx_destroy(&pr->pr_mtx); 163113275Smike FREE(pr, M_PRISON); 164113275Smike return (error); 165113275Smike} 166113275Smike 167113275Smike/* 168113275Smike * MPSAFE 169113275Smike */ 170113275Smikeint 171113275Smikejail_attach(td, uap) 172113275Smike struct thread *td; 173113275Smike struct jail_attach_args /* { 174113275Smike int jid; 175113275Smike } */ *uap; 176113275Smike{ 177113275Smike struct proc *p; 178113275Smike struct ucred *newcred, *oldcred; 179113275Smike struct prison *pr; 180113275Smike int error; 181113275Smike 182113275Smike p = td->td_proc; 183113275Smike 184113275Smike mtx_lock(&allprison_mtx); 185113275Smike pr = prison_find(uap->jid); 186113275Smike if (pr == NULL) { 187113275Smike mtx_unlock(&allprison_mtx); 188113275Smike return (EINVAL); 189113275Smike } 190113275Smike pr->pr_ref++; 191113275Smike mtx_unlock(&pr->pr_mtx); 192113275Smike mtx_unlock(&allprison_mtx); 193113275Smike 194113275Smike error = suser_cred(td->td_ucred, PRISON_ROOT); 19546155Sphk if (error) 196113275Smike goto e_dropref; 197113275Smike mtx_lock(&Giant); 198113275Smike vn_lock(pr->pr_root, LK_EXCLUSIVE | LK_RETRY, td); 199113275Smike if ((error = change_dir(pr->pr_root, td)) != 0) 200113275Smike goto e_unlock; 201113275Smike#ifdef MAC 202113275Smike if ((error = mac_check_vnode_chroot(td->td_ucred, pr->pr_root))) 203113275Smike goto e_unlock; 204113275Smike#endif 205113275Smike VOP_UNLOCK(pr->pr_root, 0, td); 206113275Smike change_root(pr->pr_root, td); 207113275Smike mtx_unlock(&Giant); 208113275Smike 20984828Sjhb newcred = crget(); 21084828Sjhb PROC_LOCK(p); 21184828Sjhb /* Implicitly fail if already in jail. */ 21293593Sjhb error = suser_cred(p->p_ucred, 0); 213113275Smike if (error) { 214113275Smike PROC_UNLOCK(p); 215113275Smike crfree(newcred); 216113275Smike goto e_dropref; 217113275Smike } 21884828Sjhb oldcred = p->p_ucred; 219113275Smike setsugid(p); 22084828Sjhb crcopy(newcred, oldcred); 221113630Sjhb newcred->cr_prison = pr; 22284828Sjhb p->p_ucred = newcred; 22384828Sjhb PROC_UNLOCK(p); 22484828Sjhb crfree(oldcred); 22546155Sphk return (0); 226113275Smikee_unlock: 227113275Smike VOP_UNLOCK(pr->pr_root, 0, td); 228113275Smike mtx_unlock(&Giant); 229113275Smikee_dropref: 230113275Smike mtx_lock(&pr->pr_mtx); 231113275Smike pr->pr_ref--; 232113275Smike mtx_unlock(&pr->pr_mtx); 23346155Sphk return (error); 23446155Sphk} 23546155Sphk 236113275Smike/* 237113275Smike * Returns a locked prison instance, or NULL on failure. 238113275Smike */ 239113275Smikestatic struct prison * 240113275Smikeprison_find(int prid) 241113275Smike{ 242113275Smike struct prison *pr; 243113275Smike 244113275Smike mtx_assert(&allprison_mtx, MA_OWNED); 245113275Smike LIST_FOREACH(pr, &allprison, pr_list) { 246113275Smike if (pr->pr_id == prid) { 247113275Smike mtx_lock(&pr->pr_mtx); 248113275Smike return (pr); 249113275Smike } 250113275Smike } 251113275Smike return (NULL); 252113275Smike} 253113275Smike 25472786Srwatsonvoid 25572786Srwatsonprison_free(struct prison *pr) 25672786Srwatson{ 25772786Srwatson 258113275Smike mtx_assert(&Giant, MA_OWNED); 259113275Smike mtx_lock(&allprison_mtx); 26087275Srwatson mtx_lock(&pr->pr_mtx); 26172786Srwatson pr->pr_ref--; 26272786Srwatson if (pr->pr_ref == 0) { 263113275Smike LIST_REMOVE(pr, pr_list); 26487275Srwatson mtx_unlock(&pr->pr_mtx); 265113275Smike prisoncount--; 266113275Smike mtx_unlock(&allprison_mtx); 267113275Smike vrele(pr->pr_root); 26887275Srwatson mtx_destroy(&pr->pr_mtx); 26972786Srwatson if (pr->pr_linux != NULL) 27072786Srwatson FREE(pr->pr_linux, M_PRISON); 27172786Srwatson FREE(pr, M_PRISON); 27287275Srwatson return; 27372786Srwatson } 27487275Srwatson mtx_unlock(&pr->pr_mtx); 275113275Smike mtx_unlock(&allprison_mtx); 27672786Srwatson} 27772786Srwatson 27872786Srwatsonvoid 27972786Srwatsonprison_hold(struct prison *pr) 28072786Srwatson{ 28172786Srwatson 28287275Srwatson mtx_lock(&pr->pr_mtx); 28372786Srwatson pr->pr_ref++; 28487275Srwatson mtx_unlock(&pr->pr_mtx); 28572786Srwatson} 28672786Srwatson 28787275Srwatsonu_int32_t 28887275Srwatsonprison_getip(struct ucred *cred) 28987275Srwatson{ 29087275Srwatson 29187275Srwatson return (cred->cr_prison->pr_ip); 29287275Srwatson} 29387275Srwatson 29446155Sphkint 29572786Srwatsonprison_ip(struct ucred *cred, int flag, u_int32_t *ip) 29646155Sphk{ 29746155Sphk u_int32_t tmp; 29846155Sphk 29972786Srwatson if (!jailed(cred)) 30046155Sphk return (0); 30146155Sphk if (flag) 30246155Sphk tmp = *ip; 30346155Sphk else 30446155Sphk tmp = ntohl(*ip); 30546155Sphk if (tmp == INADDR_ANY) { 30646155Sphk if (flag) 30772786Srwatson *ip = cred->cr_prison->pr_ip; 30846155Sphk else 30972786Srwatson *ip = htonl(cred->cr_prison->pr_ip); 31046155Sphk return (0); 31146155Sphk } 31281114Srwatson if (tmp == INADDR_LOOPBACK) { 31381114Srwatson if (flag) 31481114Srwatson *ip = cred->cr_prison->pr_ip; 31581114Srwatson else 31681114Srwatson *ip = htonl(cred->cr_prison->pr_ip); 31781114Srwatson return (0); 31881114Srwatson } 31972786Srwatson if (cred->cr_prison->pr_ip != tmp) 32046155Sphk return (1); 32146155Sphk return (0); 32246155Sphk} 32346155Sphk 32446155Sphkvoid 32572786Srwatsonprison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip) 32646155Sphk{ 32746155Sphk u_int32_t tmp; 32846155Sphk 32972786Srwatson if (!jailed(cred)) 33046155Sphk return; 33146155Sphk if (flag) 33246155Sphk tmp = *ip; 33346155Sphk else 33446155Sphk tmp = ntohl(*ip); 33581114Srwatson if (tmp == INADDR_LOOPBACK) { 33646155Sphk if (flag) 33772786Srwatson *ip = cred->cr_prison->pr_ip; 33846155Sphk else 33972786Srwatson *ip = htonl(cred->cr_prison->pr_ip); 34046155Sphk return; 34146155Sphk } 34246155Sphk return; 34346155Sphk} 34446155Sphk 34546155Sphkint 34672786Srwatsonprison_if(struct ucred *cred, struct sockaddr *sa) 34746155Sphk{ 34846155Sphk struct sockaddr_in *sai = (struct sockaddr_in*) sa; 34946155Sphk int ok; 35046155Sphk 35161235Srwatson if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only) 35261235Srwatson ok = 1; 35361235Srwatson else if (sai->sin_family != AF_INET) 35446155Sphk ok = 0; 35572786Srwatson else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr)) 35646155Sphk ok = 1; 35746155Sphk else 35846155Sphk ok = 0; 35946155Sphk return (ok); 36046155Sphk} 36172786Srwatson 36272786Srwatson/* 36372786Srwatson * Return 0 if jails permit p1 to frob p2, otherwise ESRCH. 36472786Srwatson */ 36572786Srwatsonint 36672786Srwatsonprison_check(cred1, cred2) 36772786Srwatson struct ucred *cred1, *cred2; 36872786Srwatson{ 36972786Srwatson 37072786Srwatson if (jailed(cred1)) { 37172786Srwatson if (!jailed(cred2)) 37272786Srwatson return (ESRCH); 37372786Srwatson if (cred2->cr_prison != cred1->cr_prison) 37472786Srwatson return (ESRCH); 37572786Srwatson } 37672786Srwatson 37772786Srwatson return (0); 37872786Srwatson} 37972786Srwatson 38072786Srwatson/* 38172786Srwatson * Return 1 if the passed credential is in a jail, otherwise 0. 38272786Srwatson */ 38372786Srwatsonint 38472786Srwatsonjailed(cred) 38572786Srwatson struct ucred *cred; 38672786Srwatson{ 38772786Srwatson 38872786Srwatson return (cred->cr_prison != NULL); 38972786Srwatson} 39091384Srobert 39191384Srobert/* 39291384Srobert * Return the correct hostname for the passed credential. 39391384Srobert */ 39491391Srobertvoid 39591391Srobertgetcredhostname(cred, buf, size) 39691384Srobert struct ucred *cred; 39791391Srobert char *buf; 39891391Srobert size_t size; 39991384Srobert{ 40091384Srobert 40191391Srobert if (jailed(cred)) { 40291391Srobert mtx_lock(&cred->cr_prison->pr_mtx); 403105354Srobert strlcpy(buf, cred->cr_prison->pr_host, size); 40491391Srobert mtx_unlock(&cred->cr_prison->pr_mtx); 40591391Srobert } 40691391Srobert else 407105354Srobert strlcpy(buf, hostname, size); 40891384Srobert} 409113275Smike 410113275Smikestatic int 411113275Smikesysctl_jail_list(SYSCTL_HANDLER_ARGS) 412113275Smike{ 413113275Smike struct xprison *xp, *sxp; 414113275Smike struct prison *pr; 415113275Smike int count, error; 416113275Smike 417113275Smike mtx_assert(&Giant, MA_OWNED); 418113275Smikeretry: 419113275Smike mtx_lock(&allprison_mtx); 420113275Smike count = prisoncount; 421113275Smike mtx_unlock(&allprison_mtx); 422113275Smike 423113275Smike if (count == 0) 424113275Smike return (0); 425113275Smike 426113275Smike sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); 427113275Smike mtx_lock(&allprison_mtx); 428113275Smike if (count != prisoncount) { 429113275Smike mtx_unlock(&allprison_mtx); 430113275Smike free(sxp, M_TEMP); 431113275Smike goto retry; 432113275Smike } 433113275Smike 434113275Smike LIST_FOREACH(pr, &allprison, pr_list) { 435113275Smike mtx_lock(&pr->pr_mtx); 436113275Smike xp->pr_version = XPRISON_VERSION; 437113275Smike xp->pr_id = pr->pr_id; 438113275Smike strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path)); 439113275Smike strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host)); 440113275Smike xp->pr_ip = pr->pr_ip; 441113275Smike mtx_unlock(&pr->pr_mtx); 442113275Smike xp++; 443113275Smike } 444113275Smike mtx_unlock(&allprison_mtx); 445113275Smike 446113275Smike error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count); 447113275Smike free(sxp, M_TEMP); 448113275Smike if (error) 449113275Smike return (error); 450113275Smike return (0); 451113275Smike} 452113275Smike 453113275SmikeSYSCTL_OID(_security_jail, OID_AUTO, list, CTLTYPE_STRUCT | CTLFLAG_RD, 454113275Smike NULL, 0, sysctl_jail_list, "S", "List of active jails"); 455