X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/a3519386ad102228551ee12acd6884a45e8cfc6f..6dc798958748eb3e12b0d13eb7c4c35a85af0e80:/sys/kern/kern_jail.c diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 749820f1af..bde34e7785 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -121,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() * @@ -129,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); } /* @@ -256,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); @@ -610,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--; @@ -621,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); + } +}