The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_jail.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * ----------------------------------------------------------------------------
    3  * "THE BEER-WARE LICENSE" (Revision 42):
    4  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
    5  * can do whatever you want with this stuff. If we meet some day, and you think
    6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
    7  * ----------------------------------------------------------------------------
    8  */
    9 
   10 #include <sys/cdefs.h>
   11 __FBSDID("$FreeBSD: releng/5.2/sys/kern/kern_jail.c 126029 2004-02-19 23:26:39Z nectar $");
   12 
   13 #include <sys/param.h>
   14 #include <sys/types.h>
   15 #include <sys/kernel.h>
   16 #include <sys/systm.h>
   17 #include <sys/errno.h>
   18 #include <sys/sysproto.h>
   19 #include <sys/malloc.h>
   20 #include <sys/proc.h>
   21 #include <sys/jail.h>
   22 #include <sys/lock.h>
   23 #include <sys/mutex.h>
   24 #include <sys/namei.h>
   25 #include <sys/queue.h>
   26 #include <sys/socket.h>
   27 #include <sys/syscallsubr.h>
   28 #include <sys/sysctl.h>
   29 #include <sys/vnode.h>
   30 #include <net/if.h>
   31 #include <netinet/in.h>
   32 
   33 MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
   34 
   35 SYSCTL_DECL(_security);
   36 SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW, 0,
   37     "Jail rules");
   38 
   39 mp_fixme("these variables need a lock")
   40 
   41 int     jail_set_hostname_allowed = 1;
   42 SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
   43     &jail_set_hostname_allowed, 0,
   44     "Processes in jail can set their hostnames");
   45 
   46 int     jail_socket_unixiproute_only = 1;
   47 SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
   48     &jail_socket_unixiproute_only, 0,
   49     "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
   50 
   51 int     jail_sysvipc_allowed = 0;
   52 SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
   53     &jail_sysvipc_allowed, 0,
   54     "Processes in jail can use System V IPC primitives");
   55 
   56 /* allprison, lastprid, and prisoncount are protected by allprison_mtx. */
   57 struct  prisonlist allprison;
   58 struct  mtx allprison_mtx;
   59 int     lastprid = 0;
   60 int     prisoncount = 0;
   61 
   62 static void              init_prison(void *);
   63 static struct prison    *prison_find(int);
   64 static int               sysctl_jail_list(SYSCTL_HANDLER_ARGS);
   65 
   66 static void
   67 init_prison(void *data __unused)
   68 {
   69 
   70         mtx_init(&allprison_mtx, "allprison", NULL, MTX_DEF);
   71         LIST_INIT(&allprison);
   72 }
   73 
   74 SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL);
   75 
   76 /*
   77  * MPSAFE
   78  *
   79  * struct jail_args {
   80  *      struct jail *jail;
   81  * };
   82  */
   83 int
   84 jail(struct thread *td, struct jail_args *uap)
   85 {
   86         struct nameidata nd;
   87         struct prison *pr, *tpr;
   88         struct jail j;
   89         struct jail_attach_args jaa;
   90         int error, tryprid;
   91 
   92         error = copyin(uap->jail, &j, sizeof(j));
   93         if (error)
   94                 return (error);
   95         if (j.version != 0)
   96                 return (EINVAL);
   97 
   98         MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
   99         mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF);
  100         pr->pr_ref = 1;
  101         error = copyinstr(j.path, &pr->pr_path, sizeof(pr->pr_path), 0);
  102         if (error)
  103                 goto e_killmtx;
  104         mtx_lock(&Giant);
  105         NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, pr->pr_path, td);
  106         error = namei(&nd);
  107         if (error) {
  108                 mtx_unlock(&Giant);
  109                 goto e_killmtx;
  110         }
  111         pr->pr_root = nd.ni_vp;
  112         VOP_UNLOCK(nd.ni_vp, 0, td);
  113         NDFREE(&nd, NDF_ONLY_PNBUF);
  114         mtx_unlock(&Giant);
  115         error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);
  116         if (error)
  117                 goto e_dropvnref;
  118         pr->pr_ip = j.ip_number;
  119         pr->pr_linux = NULL;
  120         pr->pr_securelevel = securelevel;
  121 
  122         /* Determine next pr_id and add prison to allprison list. */
  123         mtx_lock(&allprison_mtx);
  124         tryprid = lastprid + 1;
  125         if (tryprid == JAIL_MAX)
  126                 tryprid = 1;
  127 next:
  128         LIST_FOREACH(tpr, &allprison, pr_list) {
  129                 if (tpr->pr_id == tryprid) {
  130                         tryprid++;
  131                         if (tryprid == JAIL_MAX) {
  132                                 mtx_unlock(&allprison_mtx);
  133                                 error = EAGAIN;
  134                                 goto e_dropvnref;
  135                         }
  136                         goto next;
  137                 }
  138         }
  139         pr->pr_id = jaa.jid = lastprid = tryprid;
  140         LIST_INSERT_HEAD(&allprison, pr, pr_list);
  141         prisoncount++;
  142         mtx_unlock(&allprison_mtx);
  143 
  144         error = jail_attach(td, &jaa);
  145         if (error)
  146                 goto e_dropprref;
  147         mtx_lock(&pr->pr_mtx);
  148         pr->pr_ref--;
  149         mtx_unlock(&pr->pr_mtx);
  150         td->td_retval[0] = jaa.jid;
  151         return (0);
  152 e_dropprref:
  153         mtx_lock(&allprison_mtx);
  154         LIST_REMOVE(pr, pr_list);
  155         prisoncount--;
  156         mtx_unlock(&allprison_mtx);
  157 e_dropvnref:
  158         mtx_lock(&Giant);
  159         vrele(pr->pr_root);
  160         mtx_unlock(&Giant);
  161 e_killmtx:
  162         mtx_destroy(&pr->pr_mtx);
  163         FREE(pr, M_PRISON);
  164         return (error);
  165 }
  166 
  167 /*
  168  * MPSAFE
  169  *
  170  * struct jail_attach_args {
  171  *      int jid;
  172  * };
  173  */
  174 int
  175 jail_attach(struct thread *td, struct jail_attach_args *uap)
  176 {
  177         struct proc *p;
  178         struct ucred *newcred, *oldcred;
  179         struct prison *pr;
  180         int error;
  181         
  182         /*
  183          * XXX: Note that there is a slight race here if two threads
  184          * in the same privileged process attempt to attach to two
  185          * different jails at the same time.  It is important for
  186          * user processes not to do this, or they might end up with
  187          * a process root from one prison, but attached to the jail
  188          * of another.
  189          */
  190         error = suser(td);
  191         if (error)
  192                 return (error);
  193 
  194         p = td->td_proc;
  195         mtx_lock(&allprison_mtx);
  196         pr = prison_find(uap->jid);
  197         if (pr == NULL) {
  198                 mtx_unlock(&allprison_mtx);
  199                 return (EINVAL);
  200         }
  201         pr->pr_ref++;
  202         mtx_unlock(&pr->pr_mtx);
  203         mtx_unlock(&allprison_mtx);
  204 
  205         mtx_lock(&Giant);
  206         vn_lock(pr->pr_root, LK_EXCLUSIVE | LK_RETRY, td);
  207         if ((error = change_dir(pr->pr_root, td)) != 0)
  208                 goto e_unlock;
  209 #ifdef MAC
  210         if ((error = mac_check_vnode_chroot(td->td_ucred, pr->pr_root)))
  211                 goto e_unlock;
  212 #endif
  213         VOP_UNLOCK(pr->pr_root, 0, td);
  214         change_root(pr->pr_root, td);
  215         mtx_unlock(&Giant);
  216 
  217         newcred = crget();
  218         PROC_LOCK(p);
  219         oldcred = p->p_ucred;
  220         setsugid(p);
  221         crcopy(newcred, oldcred);
  222         newcred->cr_prison = pr;
  223         p->p_ucred = newcred;
  224         PROC_UNLOCK(p);
  225         crfree(oldcred);
  226         return (0);
  227 e_unlock:
  228         VOP_UNLOCK(pr->pr_root, 0, td);
  229         mtx_unlock(&Giant);
  230         mtx_lock(&pr->pr_mtx);
  231         pr->pr_ref--;
  232         mtx_unlock(&pr->pr_mtx);
  233         return (error);
  234 }
  235 
  236 /*
  237  * Returns a locked prison instance, or NULL on failure.
  238  */
  239 static struct prison *
  240 prison_find(int prid)
  241 {
  242         struct prison *pr;
  243 
  244         mtx_assert(&allprison_mtx, MA_OWNED);
  245         LIST_FOREACH(pr, &allprison, pr_list) {
  246                 if (pr->pr_id == prid) {
  247                         mtx_lock(&pr->pr_mtx);
  248                         return (pr);
  249                 }
  250         }
  251         return (NULL);
  252 }
  253 
  254 void
  255 prison_free(struct prison *pr)
  256 {
  257 
  258         mtx_assert(&Giant, MA_OWNED);
  259         mtx_lock(&allprison_mtx);
  260         mtx_lock(&pr->pr_mtx);
  261         pr->pr_ref--;
  262         if (pr->pr_ref == 0) {
  263                 LIST_REMOVE(pr, pr_list);
  264                 mtx_unlock(&pr->pr_mtx);
  265                 prisoncount--;
  266                 mtx_unlock(&allprison_mtx);
  267                 vrele(pr->pr_root);
  268                 mtx_destroy(&pr->pr_mtx);
  269                 if (pr->pr_linux != NULL)
  270                         FREE(pr->pr_linux, M_PRISON);
  271                 FREE(pr, M_PRISON);
  272                 return;
  273         }
  274         mtx_unlock(&pr->pr_mtx);
  275         mtx_unlock(&allprison_mtx);
  276 }
  277 
  278 void
  279 prison_hold(struct prison *pr)
  280 {
  281 
  282         mtx_lock(&pr->pr_mtx);
  283         pr->pr_ref++;
  284         mtx_unlock(&pr->pr_mtx);
  285 }
  286 
  287 u_int32_t
  288 prison_getip(struct ucred *cred)
  289 {
  290 
  291         return (cred->cr_prison->pr_ip);
  292 }
  293 
  294 int
  295 prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
  296 {
  297         u_int32_t tmp;
  298 
  299         if (!jailed(cred))
  300                 return (0);
  301         if (flag) 
  302                 tmp = *ip;
  303         else
  304                 tmp = ntohl(*ip);
  305         if (tmp == INADDR_ANY) {
  306                 if (flag) 
  307                         *ip = cred->cr_prison->pr_ip;
  308                 else
  309                         *ip = htonl(cred->cr_prison->pr_ip);
  310                 return (0);
  311         }
  312         if (tmp == INADDR_LOOPBACK) {
  313                 if (flag)
  314                         *ip = cred->cr_prison->pr_ip;
  315                 else
  316                         *ip = htonl(cred->cr_prison->pr_ip);
  317                 return (0);
  318         }
  319         if (cred->cr_prison->pr_ip != tmp)
  320                 return (1);
  321         return (0);
  322 }
  323 
  324 void
  325 prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip)
  326 {
  327         u_int32_t tmp;
  328 
  329         if (!jailed(cred))
  330                 return;
  331         if (flag)
  332                 tmp = *ip;
  333         else
  334                 tmp = ntohl(*ip);
  335         if (tmp == INADDR_LOOPBACK) {
  336                 if (flag)
  337                         *ip = cred->cr_prison->pr_ip;
  338                 else
  339                         *ip = htonl(cred->cr_prison->pr_ip);
  340                 return;
  341         }
  342         return;
  343 }
  344 
  345 int
  346 prison_if(struct ucred *cred, struct sockaddr *sa)
  347 {
  348         struct sockaddr_in *sai;
  349         int ok;
  350 
  351         sai = (struct sockaddr_in *)sa;
  352         if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
  353                 ok = 1;
  354         else if (sai->sin_family != AF_INET)
  355                 ok = 0;
  356         else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr))
  357                 ok = 1;
  358         else
  359                 ok = 0;
  360         return (ok);
  361 }
  362 
  363 /*
  364  * Return 0 if jails permit p1 to frob p2, otherwise ESRCH.
  365  */
  366 int
  367 prison_check(struct ucred *cred1, struct ucred *cred2)
  368 {
  369 
  370         if (jailed(cred1)) {
  371                 if (!jailed(cred2))
  372                         return (ESRCH);
  373                 if (cred2->cr_prison != cred1->cr_prison)
  374                         return (ESRCH);
  375         }
  376 
  377         return (0);
  378 }
  379 
  380 /*
  381  * Return 1 if the passed credential is in a jail, otherwise 0.
  382  */
  383 int
  384 jailed(struct ucred *cred)
  385 {
  386 
  387         return (cred->cr_prison != NULL);
  388 }
  389 
  390 /*
  391  * Return the correct hostname for the passed credential.
  392  */
  393 void
  394 getcredhostname(struct ucred *cred, char *buf, size_t size)
  395 {
  396 
  397         if (jailed(cred)) {
  398                 mtx_lock(&cred->cr_prison->pr_mtx);
  399                 strlcpy(buf, cred->cr_prison->pr_host, size);
  400                 mtx_unlock(&cred->cr_prison->pr_mtx);
  401         } else
  402                 strlcpy(buf, hostname, size);
  403 }
  404 
  405 static int
  406 sysctl_jail_list(SYSCTL_HANDLER_ARGS)
  407 {
  408         struct xprison *xp, *sxp;
  409         struct prison *pr;
  410         int count, error;
  411 
  412         mtx_assert(&Giant, MA_OWNED);
  413 retry:
  414         mtx_lock(&allprison_mtx);
  415         count = prisoncount;
  416         mtx_unlock(&allprison_mtx);
  417 
  418         if (count == 0)
  419                 return (0);
  420 
  421         sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO);
  422         mtx_lock(&allprison_mtx);
  423         if (count != prisoncount) {
  424                 mtx_unlock(&allprison_mtx);
  425                 free(sxp, M_TEMP);
  426                 goto retry;
  427         }
  428         
  429         LIST_FOREACH(pr, &allprison, pr_list) {
  430                 mtx_lock(&pr->pr_mtx);
  431                 xp->pr_version = XPRISON_VERSION;
  432                 xp->pr_id = pr->pr_id;
  433                 strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path));
  434                 strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host));
  435                 xp->pr_ip = pr->pr_ip;
  436                 mtx_unlock(&pr->pr_mtx);
  437                 xp++;
  438         }
  439         mtx_unlock(&allprison_mtx);
  440 
  441         error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count);
  442         free(sxp, M_TEMP);
  443         if (error)
  444                 return (error);
  445         return (0);
  446 }
  447 
  448 SYSCTL_OID(_security_jail, OID_AUTO, list, CTLTYPE_STRUCT | CTLFLAG_RD,
  449     NULL, 0, sysctl_jail_list, "S", "List of active jails");

Cache object: 3ef287d58e761d55fa7ebeca1e5d1ee2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.