priv: Use PRIV_VFS_CHFLAGS_DEV
[dragonfly.git] / sys / kern / kern_jail.c
index 3addf6d..bde34e7 100644 (file)
@@ -36,7 +36,7 @@
 
 /*
  * $FreeBSD: src/sys/kern/kern_jail.c,v 1.6.2.3 2001/08/17 01:00:26 rwatson Exp $
- * $DragonFly: src/sys/kern/kern_jail.c,v 1.18 2007/02/16 23:41:02 victor Exp $
+ * $DragonFly: src/sys/kern/kern_jail.c,v 1.19 2008/05/17 18:20:33 dillon Exp $
  */
 
 #include "opt_inet6.h"
@@ -51,6 +51,7 @@
 #include <sys/nlookup.h>
 #include <sys/namecache.h>
 #include <sys/proc.h>
+#include <sys/priv.h>
 #include <sys/jail.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
@@ -87,6 +88,11 @@ SYSCTL_INT(_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,
     &jail_chflags_allowed, 0,
     "Process in jail can set chflags(1)");
 
+int    jail_allow_raw_sockets = 0;
+SYSCTL_INT(_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,
+    &jail_allow_raw_sockets, 0,
+    "Process in jail can create raw sockets");
+
 int    lastprid = 0;
 int    prisoncount = 0;
 
@@ -116,6 +122,70 @@ kern_jail_attach(int jid)
        return(0);
 }
 
+static int
+assign_prison_id(struct prison *pr)
+{
+       int tryprid;
+       struct prison *tpr;
+
+       tryprid = lastprid + 1;
+       if (tryprid == JAIL_MAX)
+               tryprid = 1;
+next:
+       LIST_FOREACH(tpr, &allprison, pr_list) {
+               if (tpr->pr_id != tryprid)
+                       continue;
+               tryprid++;
+               if (tryprid == JAIL_MAX) {
+                       return (ERANGE);
+               }
+               goto next;
+       }
+       pr->pr_id = lastprid = tryprid;
+
+       return (0);
+}
+
+static int
+kern_jail(struct prison *pr, struct jail *j)
+{
+       int error;
+       struct nlookupdata nd;
+
+       error = nlookup_init(&nd, j->path, UIO_USERSPACE, NLC_FOLLOW);
+       if (error) {
+               nlookup_done(&nd);
+               return (error);
+       }
+       error = nlookup(&nd);
+       if (error) {
+               nlookup_done(&nd);
+               return (error);
+       }
+       cache_copy(&nd.nl_nch, &pr->pr_root);
+
+       varsymset_init(&pr->pr_varsymset, NULL);
+       prison_ipcache_init(pr);
+
+       error = assign_prison_id(pr);
+       if (error) {
+               varsymset_clean(&pr->pr_varsymset);
+               nlookup_done(&nd);
+               return (error);
+       }
+               
+       LIST_INSERT_HEAD(&allprison, pr, pr_list);
+       prisoncount++;
+
+       error = kern_jail_attach(pr->pr_id);
+       if (error) {
+               LIST_REMOVE(pr, pr_list);
+               varsymset_clean(&pr->pr_varsymset);
+       }
+       nlookup_done(&nd);
+       return (error);
+}
+
 /*
  * jail()
  *
@@ -124,122 +194,95 @@ kern_jail_attach(int jid)
 int
 sys_jail(struct jail_args *uap)
 {
-       struct prison *pr, *tpr;
-       struct jail j;
-       struct jail_v0 jv0;
        struct thread *td = curthread;
-       int error, tryprid, i;
-       uint32_t jversion;
-       struct nlookupdata nd;
-       /* Multiip */
-       struct sockaddr_storage *uips; /* Userland ips */
-       struct sockaddr_in ip4addr;
+       struct prison *pr;
        struct jail_ip_storage *jip;
-       /* Multiip */
+       struct jail j;
+       int error;
+       uint32_t jversion;
 
-       error = suser(td);
-       if (error) {
-               uap->sysmsg_result = -1;
-               return(error);
-       }
-       error = copyin(uap->jail, &jversion, sizeof jversion);
-       if (error) {
-               uap->sysmsg_result = -1;
-               return(error);
-       }
-       pr = kmalloc(sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
+       uap->sysmsg_result = -1;
+
+       error = priv_check(td, PRIV_JAIL_CREATE);
+       if (error)
+               return (error);
+
+       error = copyin(uap->jail, &jversion, sizeof(jversion));
+       if (error)
+               return (error);
+
+       pr = kmalloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
        SLIST_INIT(&pr->pr_ips);
 
        switch (jversion) {
        case 0:
-               error = copyin(uap->jail, &jv0, sizeof(struct jail_v0));
+               /* Single IPv4 jails. */
+               {
+               struct jail_v0 jv0;
+               struct sockaddr_in ip4addr;
+
+               error = copyin(uap->jail, &jv0, sizeof(jv0));
                if (error)
-                       goto bail;
+                       goto out;
+
+               j.path = jv0.path;
+               j.hostname = jv0.hostname; 
+
                jip = kmalloc(sizeof(*jip),  M_PRISON, M_WAITOK | M_ZERO);
                ip4addr.sin_family = AF_INET;
                ip4addr.sin_addr.s_addr = htonl(jv0.ip_number);
                memcpy(&jip->ip, &ip4addr, sizeof(ip4addr));
                SLIST_INSERT_HEAD(&pr->pr_ips, jip, entries);
                break;
+               }
+
        case 1:
+               /*
+                * DragonFly multi noIP/IPv4/IPv6 jails
+                *
+                * NOTE: This version is unsupported by FreeBSD
+                * (which uses version 2 instead).
+                */
+
                error = copyin(uap->jail, &j, sizeof(j));
                if (error)
-                       goto bail;
-               uips = kmalloc((sizeof(*uips) * j.n_ips), M_PRISON,
-                               M_WAITOK | M_ZERO);
-               error = copyin(j.ips, uips, (sizeof(*uips) * j.n_ips));
-               if (error) {
-                       kfree(uips, M_PRISON);
-                       goto bail;
-               }
-               for (i = 0; i < j.n_ips; i++) {
-                       jip = kmalloc(sizeof(*jip),  M_PRISON,
+                       goto out;
+
+               for (int i = 0; i < j.n_ips; i++) {
+                       jip = kmalloc(sizeof(*jip), M_PRISON,
                                      M_WAITOK | M_ZERO);
-                       memcpy(&jip->ip, &uips[i], sizeof(*uips));
                        SLIST_INSERT_HEAD(&pr->pr_ips, jip, entries);
+                       error = copyin(&j.ips[i], &jip->ip,
+                                       sizeof(struct sockaddr_storage));
+                       if (error)
+                               goto out;
                }
-               kfree(uips, M_PRISON);
                break;
        default:
                error = EINVAL;
-               goto bail;
+               goto out;
        }
 
-       error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
-       if (error)
-               goto bail;
-       error = nlookup_init(&nd, j.path, UIO_USERSPACE, NLC_FOLLOW);
+       error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);
        if (error)
-               goto nlookup_init_clean;
-       error = nlookup(&nd);
-       if (error)
-               goto nlookup_init_clean;
-       cache_copy(&nd.nl_nch, &pr->pr_root);
-
-       varsymset_init(&pr->pr_varsymset, NULL);
-       prison_ipcache_init(pr);
-
-       tryprid = lastprid + 1;
-       if (tryprid == JAIL_MAX)
-               tryprid = 1;
-next:
-       LIST_FOREACH(tpr, &allprison, pr_list) {
-               if (tpr->pr_id != tryprid)
-                       continue;
-               tryprid++;
-               if (tryprid == JAIL_MAX) {
-                       error = ERANGE;
-                       goto varsym_clean;
-               }
-               goto next;
-       }
-       pr->pr_id = lastprid = tryprid;
-       LIST_INSERT_HEAD(&allprison, pr, pr_list);
-       prisoncount++;
+               goto out;
 
-       error = kern_jail_attach(pr->pr_id);
+       error = kern_jail(pr, &j);
        if (error)
-               goto jail_attach_clean;
+               goto out;
 
-       nlookup_done(&nd);
        uap->sysmsg_result = pr->pr_id;
        return (0);
 
-jail_attach_clean:
-       LIST_REMOVE(pr, pr_list);
-varsym_clean:
-       varsymset_clean(&pr->pr_varsymset);
-nlookup_init_clean:
-       nlookup_done(&nd);
-bail:
+out:
        /* Delete all ips */
        while (!SLIST_EMPTY(&pr->pr_ips)) {
                jip = SLIST_FIRST(&pr->pr_ips);
                SLIST_REMOVE_HEAD(&pr->pr_ips, entries);
-               FREE(jip, M_PRISON);
+               kfree(jip, M_PRISON);
        }
-       FREE(pr, M_PRISON);
-       return(error);
+       kfree(pr, M_PRISON);
+       return (error);
 }
 
 /*
@@ -251,7 +294,7 @@ sys_jail_attach(struct jail_attach_args *uap)
        struct thread *td = curthread;
        int error;
 
-       error = suser(td);
+       error = priv_check(td, PRIV_JAIL_ATTACH);
        if (error)
                return(error);
 
@@ -605,7 +648,7 @@ prison_free(struct prison *pr)
        while (!SLIST_EMPTY(&pr->pr_ips)) {
                jls = SLIST_FIRST(&pr->pr_ips);
                SLIST_REMOVE_HEAD(&pr->pr_ips, entries);
-               FREE(jls, M_PRISON);
+               kfree(jls, M_PRISON);
        }
        LIST_REMOVE(pr, pr_list);
        prisoncount--;
@@ -616,3 +659,46 @@ prison_free(struct prison *pr)
        cache_drop(&pr->pr_root);
        kfree(pr, M_PRISON);
 }
+
+/*
+ * Check if permisson for a specific privilege is granted within jail.
+ */
+int
+prison_priv_check(struct ucred *cred, int priv)
+{
+       if (!jailed(cred))
+               return (0);
+
+       switch (priv) {
+       case PRIV_CRED_SETUID:
+       case PRIV_CRED_SETEUID:
+       case PRIV_CRED_SETGID:
+       case PRIV_CRED_SETEGID:
+       case PRIV_CRED_SETGROUPS:
+       case PRIV_CRED_SETREUID:
+       case PRIV_CRED_SETREGID:
+       case PRIV_CRED_SETRESUID:
+       case PRIV_CRED_SETRESGID:
+
+       case PRIV_VFS_SYSFLAGS:
+       case PRIV_VFS_CHOWN:
+       case PRIV_VFS_CHMOD:
+       case PRIV_VFS_CHROOT:
+       case PRIV_VFS_LINK:
+       case PRIV_VFS_CHFLAGS_DEV:
+       case PRIV_VFS_MKNOD_BAD:
+       case PRIV_VFS_MKNOD_WHT:
+       case PRIV_VFS_MKNOD_DIR:
+
+       case PRIV_PROC_SETRLIMIT:
+       case PRIV_PROC_SETLOGIN:
+
+       case PRIV_SYSCTL_WRITEJAIL:
+
+               return (0);
+
+       default:
+
+               return (EPERM);
+       }
+}