From 2dd7612536892d1499fe8dc9882602171be66d9d Mon Sep 17 00:00:00 2001
From: Antonio Huete Jimenez <tuxillo@quantumachine.net>
Date: Wed, 21 Aug 2019 02:35:42 +0200
Subject: [PATCH] jails WIP

---
 sys/kern/kern_jail.c   | 219 ++++++++++++++++++++++++++++++++++++++---
 sys/kern/kern_mib.c    |   7 +-
 sys/kern/sysv_msg.c    |  12 ++-
 sys/kern/sysv_sem.c    |   9 +-
 sys/kern/sysv_shm.c    |  13 ++-
 sys/kern/uipc_socket.c |   3 +-
 sys/kern/vfs_helper.c  |   2 +-
 sys/sys/jail.h         |  15 +++
 8 files changed, 251 insertions(+), 29 deletions(-)

diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index 4363406700..bce0d765d6 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -54,7 +54,6 @@
 #include <sys/priv.h>
 #include <sys/jail.h>
 #include <sys/socket.h>
-#include <sys/sysctl.h>
 #include <sys/kern_syscall.h>
 #include <net/if.h>
 #include <netinet/in.h>
@@ -68,30 +67,33 @@ static void		prison_ipcache_init(struct prison *);
 MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
 
 SYSCTL_NODE(, OID_AUTO, jail, CTLFLAG_RW, 0,
-    "Jail rules");
+    "All jails settings");
+
+SYSCTL_NODE(_jail, OID_AUTO, defaults, CTLFLAG_RW, 0,
+    "Default options for jails");
 
 int	jail_set_hostname_allowed = 1;
-SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
+SYSCTL_INT(_jail_defaults, 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,
+SYSCTL_INT(_jail_defaults, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
     &jail_socket_unixiproute_only, 0,
     "Processes in jail are limited to creating UNIX/IPv[46]/route sockets only");
 
 int	jail_sysvipc_allowed = 0;
-SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
+SYSCTL_INT(_jail_defaults, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
     &jail_sysvipc_allowed, 0,
     "Processes in jail can use System V IPC primitives");
 
 int    jail_chflags_allowed = 0;
-SYSCTL_INT(_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,
+SYSCTL_INT(_jail_defaults, OID_AUTO, chflags_allowed, CTLFLAG_RW,
     &jail_chflags_allowed, 0,
     "Processes in jail can alter system file flags");
 
 int    jail_allow_raw_sockets = 0;
-SYSCTL_INT(_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,
+SYSCTL_INT(_jail_defaults, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,
     &jail_allow_raw_sockets, 0,
     "Process in jail can create raw sockets");
 
@@ -178,16 +180,28 @@ kern_jail(struct prison *pr, struct jail *j)
 		nlookup_done(&nd);
 		return (error);
 	}
-		
+
 	LIST_INSERT_HEAD(&allprison, pr, pr_list);
 	++prisoncount;
 
+	error = prison_sysctl_create(pr);
+	if (error)
+		goto out;
+
 	error = kern_jail_attach(pr->pr_id);
-	if (error) {
-		LIST_REMOVE(pr, pr_list);
-		--prisoncount;
-		varsymset_clean(&pr->pr_varsymset);
-	}
+	if (error)
+		goto out2;
+
+	nlookup_done(&nd);
+	return 0;
+
+out2:
+	prison_sysctl_done(pr);
+
+out:
+	LIST_REMOVE(pr, pr_list);
+	--prisoncount;
+	varsymset_clean(&pr->pr_varsymset);
 	nlookup_done(&nd);
 	return (error);
 }
@@ -276,6 +290,13 @@ sys_jail(struct jail_args *uap)
 	if (error)
 		goto out;
 
+	/* Global settings are the default for all jails */
+	pr->pr_set_hostname_allowed = jail_set_hostname_allowed;
+	pr->pr_socket_unixiproute_only = jail_socket_unixiproute_only;
+	pr->pr_sysvipc_allowed = jail_sysvipc_allowed;
+	pr->pr_chflags_allowed = jail_chflags_allowed;
+	pr->pr_allow_raw_sockets = jail_allow_raw_sockets;
+
 	error = kern_jail(pr, &j);
 	if (error)
 		goto out;
@@ -709,6 +730,10 @@ prison_free(struct prison *pr)
 	if (pr->pr_linux != NULL)
 		kfree(pr->pr_linux, M_PRISON);
 	varsymset_clean(&pr->pr_varsymset);
+
+	/* Release the sysctl tree */
+	prison_sysctl_done(pr);
+
 	cache_drop(&pr->pr_root);
 	kfree(pr, M_PRISON);
 }
@@ -721,6 +746,8 @@ prison_free(struct prison *pr)
 int
 prison_priv_check(struct ucred *cred, int priv)
 {
+	struct prison *pr = cred->cr_prison;
+
 	if (!jailed(cred))
 		return (0);
 
@@ -784,7 +811,7 @@ prison_priv_check(struct ucred *cred, int priv)
 		 * Conditionally allow creating raw sockets in jail.
 		 */
 	case PRIV_NETINET_RAW:
-		if (jail_allow_raw_sockets)
+		if (pr->pr_allow_raw_sockets)
 			return (0);
 		else
 			return (EPERM);
@@ -797,3 +824,167 @@ prison_priv_check(struct ucred *cred, int priv)
 		return (EPERM);
 	}
 }
+
+static int
+sysctl_prison_switch_int8(SYSCTL_HANDLER_ARGS, int8_t *val)
+{
+	int new_val;
+	int error;
+
+	new_val = *val;
+
+	error = sysctl_handle_int(oidp, &new_val, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return error;
+
+	if (new_val != 0 && new_val != 1)
+		return EINVAL;
+
+	*val = new_val;
+
+	return error;
+
+}
+
+static int
+sysctl_prison_set_hostname_allowed(SYSCTL_HANDLER_ARGS)
+{
+	struct prison *pr;
+	int error;
+
+	if (arg1 == NULL)
+		return EINVAL;
+	pr = arg1;
+
+	error = sysctl_prison_switch_int8(oidp, arg1, arg2, req,
+	    &pr->pr_set_hostname_allowed);
+
+	return error;
+}
+
+static int
+sysctl_prison_socket_unixiproute_only(SYSCTL_HANDLER_ARGS)
+{
+	struct prison *pr;
+	int error;
+
+	if (arg1 == NULL)
+		return EINVAL;
+	pr = arg1;
+
+	error = sysctl_prison_switch_int8(oidp, arg1, arg2, req,
+	    &pr->pr_socket_unixiproute_only);
+
+	return error;
+}
+
+static int
+sysctl_prison_sysvipc_allowed(SYSCTL_HANDLER_ARGS)
+{
+	struct prison *pr;
+	int error;
+
+	if (arg1 == NULL)
+		return EINVAL;
+	pr = arg1;
+
+	error = sysctl_prison_switch_int8(oidp, arg1, arg2, req,
+	    &pr->pr_sysvipc_allowed);
+
+	return error;
+}
+
+static int
+sysctl_prison_chflags_allowed(SYSCTL_HANDLER_ARGS)
+{
+	struct prison *pr;
+	int error;
+
+	if (arg1 == NULL)
+		return EINVAL;
+	pr = arg1;
+
+	error = sysctl_prison_switch_int8(oidp, arg1, arg2, req,
+	    &pr->pr_chflags_allowed);
+
+	return error;
+}
+
+static int
+sysctl_prison_allow_raw_sockets(SYSCTL_HANDLER_ARGS)
+{
+	struct prison *pr;
+	int error;
+
+	if (arg1 == NULL)
+		return EINVAL;
+	pr = arg1;
+
+	error = sysctl_prison_switch_int8(oidp, arg1, arg2, req,
+	    &pr->pr_allow_raw_sockets);
+
+	return error;
+}
+
+/*
+ * Create a per-jail sysctl tree to control the prison
+ */
+int
+prison_sysctl_create(struct prison *pr)
+{
+	char id_str[7];
+
+	ksnprintf(id_str, 6, "%d", pr->pr_id);
+
+	sysctl_ctx_init(&pr->pr_sysctl_ctx);
+
+	/* Main jail node */
+	pr->pr_sysctl_tree = SYSCTL_ADD_NODE(&pr->pr_sysctl_ctx,
+	    SYSCTL_STATIC_CHILDREN(_jail),
+	    OID_AUTO, id_str, CTLFLAG_RD, 0,
+	    "Jail specific settings");
+
+	SYSCTL_ADD_PROC(&pr->pr_sysctl_ctx,
+	    SYSCTL_CHILDREN(pr->pr_sysctl_tree), OID_AUTO,
+	    "set_hostname_allowed", CTLTYPE_INT | CTLFLAG_RW,
+	    pr, sizeof(pr->pr_set_hostname_allowed),
+	    sysctl_prison_set_hostname_allowed, "I",
+	    "Processes in jail can set their hostnames");
+	SYSCTL_ADD_PROC(&pr->pr_sysctl_ctx,
+	    SYSCTL_CHILDREN(pr->pr_sysctl_tree), OID_AUTO,
+	    "socket_unixiproute_only", CTLTYPE_INT | CTLFLAG_RW,
+	    pr, sizeof(pr->pr_socket_unixiproute_only),
+	    sysctl_prison_socket_unixiproute_only, "I",
+	    "Processes in jail are limited to creating UNIX/IPv[46]/route sockets only");
+	SYSCTL_ADD_PROC(&pr->pr_sysctl_ctx,
+	    SYSCTL_CHILDREN(pr->pr_sysctl_tree), OID_AUTO,
+	    "sysvipc_allowed", CTLTYPE_INT | CTLFLAG_RW,
+	    pr, sizeof(pr->pr_sysvipc_allowed),
+	    sysctl_prison_sysvipc_allowed, "I",
+	    "Processes in jail can use System V IPC primitives");
+	SYSCTL_ADD_PROC(&pr->pr_sysctl_ctx,
+	    SYSCTL_CHILDREN(pr->pr_sysctl_tree), OID_AUTO,
+	    "chflags_allowed", CTLTYPE_INT | CTLFLAG_RW,
+	    pr, sizeof(pr->pr_chflags_allowed),
+	    sysctl_prison_chflags_allowed, "I",
+	    "Processes in jail can alter system file flags");
+	SYSCTL_ADD_PROC(&pr->pr_sysctl_ctx,
+	    SYSCTL_CHILDREN(pr->pr_sysctl_tree), OID_AUTO,
+	    "allow_raw_sockets", CTLTYPE_INT | CTLFLAG_RW,
+	    pr, sizeof(pr->pr_allow_raw_sockets),
+	    sysctl_prison_allow_raw_sockets, "I",
+	    "Process in jail can create raw sockets");
+
+	return 0;
+}
+
+int
+prison_sysctl_done(struct prison *pr)
+{
+	if (pr->pr_sysctl_tree) {
+		sysctl_ctx_free(&pr->pr_sysctl_ctx);
+		pr->pr_sysctl_tree = NULL;
+	}
+
+	return 0;
+}
diff --git a/sys/kern/kern_mib.c b/sys/kern/kern_mib.c
index 39bae529ad..d487bd0cdb 100644
--- a/sys/kern/kern_mib.c
+++ b/sys/kern/kern_mib.c
@@ -162,14 +162,17 @@ sysctl_hostname(SYSCTL_HANDLER_ARGS)
 {
 	struct thread *td = req->td;
 	struct proc *p = td ? td->td_proc : NULL;
+	struct prison *pr;
 	int error;
 
 	if (req->newptr) {
 		SYSCTL_SUNLOCK();
 		SYSCTL_XLOCK();
 	}
-	if (p && p->p_ucred->cr_prison) {
-		if (!jail_set_hostname_allowed && req->newptr)
+	if (p)
+		pr = p->p_ucred->cr_prison;
+	if (p && pr) {
+		if (!pr->pr_set_hostname_allowed && req->newptr)
 			return(EPERM);
 		error = sysctl_handle_string(oidp, 
 		    p->p_ucred->cr_prison->pr_host,
diff --git a/sys/kern/sysv_msg.c b/sys/kern/sysv_msg.c
index afe478796d..58bf570b54 100644
--- a/sys/kern/sysv_msg.c
+++ b/sys/kern/sysv_msg.c
@@ -204,6 +204,7 @@ sys_msgctl(struct msgctl_args *uap)
 {
 	struct thread *td = curthread;
 	struct proc *p = td->td_proc;
+	struct prison *pr = p->p_ucred->cr_prison;
 	int msqid = uap->msqid;
 	int cmd = uap->cmd;
 	struct msqid_ds *user_msqptr = uap->buf;
@@ -215,7 +216,7 @@ sys_msgctl(struct msgctl_args *uap)
 	kprintf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
 #endif
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	lwkt_gettoken(&msg_token);
@@ -344,6 +345,7 @@ int
 sys_msgget(struct msgget_args *uap)
 {
 	struct thread *td = curthread;
+	struct prison *pr = td->td_proc->p_ucred->cr_prison;
 	int msqid, eval;
 	int key = uap->key;
 	int msgflg = uap->msgflg;
@@ -353,7 +355,7 @@ sys_msgget(struct msgget_args *uap)
 #ifdef MSG_DEBUG_OK
 	kprintf("msgget(0x%x, 0%o)\n", key, msgflg);
 #endif
-	if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	eval = 0;
@@ -454,6 +456,7 @@ int
 sys_msgsnd(struct msgsnd_args *uap)
 {
 	struct thread *td = curthread;
+	struct prison *pr = td->td_proc->p_ucred->cr_prison;
 	int msqid = uap->msqid;
 	const void *user_msgp = uap->msgp;
 	size_t msgsz = uap->msgsz;
@@ -468,7 +471,7 @@ sys_msgsnd(struct msgsnd_args *uap)
 	    msgflg);
 #endif
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	lwkt_gettoken(&msg_token);
@@ -785,6 +788,7 @@ int
 sys_msgrcv(struct msgrcv_args *uap)
 {
 	struct thread *td = curthread;
+	struct prison *pr = td->td_proc->p_ucred->cr_prison;
 	int msqid = uap->msqid;
 	void *user_msgp = uap->msgp;
 	size_t msgsz = uap->msgsz;
@@ -801,7 +805,7 @@ sys_msgrcv(struct msgrcv_args *uap)
 	    msgsz, msgtyp, msgflg);
 #endif
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	lwkt_gettoken(&msg_token);
diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c
index 6257fd6a52..5891b4d41f 100644
--- a/sys/kern/sysv_sem.c
+++ b/sys/kern/sysv_sem.c
@@ -340,6 +340,7 @@ int
 sys___semctl(struct __semctl_args *uap)
 {
 	struct thread *td = curthread;
+	struct prison *pr = td->td_proc->p_ucred->cr_prison;
 	int semid = uap->semid;
 	int semnum = uap->semnum;
 	int cmd = uap->cmd;
@@ -355,7 +356,7 @@ sys___semctl(struct __semctl_args *uap)
 	kprintf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg);
 #endif
 
-	if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	semid = IPCID_TO_IX(semid);
@@ -566,6 +567,7 @@ int
 sys_semget(struct semget_args *uap)
 {
 	struct thread *td = curthread;
+	struct prison *pr = td->td_proc->p_ucred->cr_prison;
 	int semid, eval;
 	int key = uap->key;
 	int nsems = uap->nsems;
@@ -576,7 +578,7 @@ sys_semget(struct semget_args *uap)
 	kprintf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
 #endif
 
-	if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	eval = 0;
@@ -719,6 +721,7 @@ int
 sys_semop(struct semop_args *uap)
 {
 	struct thread *td = curthread;
+	struct prison *pr = td->td_proc->p_ucred->cr_prison;
 	int semid = uap->semid;
 	u_int nsops = uap->nsops;
 	struct sembuf sops[MAX_SOPS];
@@ -732,7 +735,7 @@ sys_semop(struct semop_args *uap)
 #ifdef SEM_DEBUG
 	kprintf("call to semop(%d, 0x%x, %u)\n", semid, sops, nsops);
 #endif
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c
index 0acf2abccd..52a361431e 100644
--- a/sys/kern/sysv_shm.c
+++ b/sys/kern/sysv_shm.c
@@ -224,10 +224,12 @@ sys_shmdt(struct shmdt_args *uap)
 	struct thread *td = curthread;
 	struct proc *p = td->td_proc;
 	struct shmmap_state *shmmap_s;
+	struct prison *pr = p->p_ucred->cr_prison;
+
 	long i;
 	int error;
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	lwkt_gettoken(&shm_token);
@@ -259,6 +261,7 @@ sys_shmat(struct shmat_args *uap)
 {
 	struct thread *td = curthread;
 	struct proc *p = td->td_proc;
+	struct prison *pr = p->p_ucred->cr_prison;
 	int error, flags;
 	long i;
 	struct shmid_ds *shmseg;
@@ -270,7 +273,7 @@ sys_shmat(struct shmat_args *uap)
 	vm_size_t align;
 	int rv;
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	lwkt_gettoken(&shm_token);
@@ -397,11 +400,12 @@ sys_shmctl(struct shmctl_args *uap)
 {
 	struct thread *td = curthread;
 	struct proc *p = td->td_proc;
+	struct prison *pr = p->p_ucred->cr_prison;
 	int error;
 	struct shmid_ds inbuf;
 	struct shmid_ds *shmseg;
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	lwkt_gettoken(&shm_token);
@@ -604,9 +608,10 @@ sys_shmget(struct shmget_args *uap)
 {
 	struct thread *td = curthread;
 	struct proc *p = td->td_proc;
+	struct prison *pr = p->p_ucred->cr_prison;
 	int segnum, mode, error;
 
-	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
+	if (pr && !pr->pr_sysvipc_allowed)
 		return (ENOSYS);
 
 	mode = uap->shmflg & ACCESSPERMS;
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 765ca5d01d..d5e53ae56f 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -206,6 +206,7 @@ socreate(int dom, struct socket **aso, int type,
 	struct protosw *prp;
 	struct socket *so;
 	struct pru_attach_info ai;
+	struct prison *pr = p->p_ucred->cr_prison;
 	int error;
 
 	if (proto)
@@ -216,7 +217,7 @@ socreate(int dom, struct socket **aso, int type,
 	if (prp == NULL || prp->pr_usrreqs->pru_attach == 0)
 		return (EPROTONOSUPPORT);
 
-	if (p->p_ucred->cr_prison && jail_socket_unixiproute_only &&
+	if (pr && pr->pr_socket_unixiproute_only &&
 	    prp->pr_domain->dom_family != PF_LOCAL &&
 	    prp->pr_domain->dom_family != PF_INET &&
 	    prp->pr_domain->dom_family != PF_INET6 &&
diff --git a/sys/kern/vfs_helper.c b/sys/kern/vfs_helper.c
index d582e046bc..f56d36f915 100644
--- a/sys/kern/vfs_helper.c
+++ b/sys/kern/vfs_helper.c
@@ -182,7 +182,7 @@ vop_helper_setattr_flags(u_int32_t *ino_flags, u_int32_t vaflags,
 		return(error);
 	}
 	if (cred->cr_uid == 0 &&
-	    (!jailed(cred)|| jail_chflags_allowed)) {
+	    (!jailed(cred) || cred->cr_prison->pr_chflags_allowed)) {
 		if ((*ino_flags & (SF_NOUNLINK|SF_IMMUTABLE|SF_APPEND)) &&
 		    securelevel > 0)
 			return (EPERM);
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
index d81b1e10b4..1b04dfecb8 100644
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -27,6 +27,10 @@
 #ifndef _NET_IF_H_
 #include <net/if.h>
 #endif
+#ifndef _SYS_SYSCTL_H_
+#include <sys/sysctl.h>
+#endif
+
 
 struct jail {
 	uint32_t	version;
@@ -96,6 +100,15 @@ struct prison {
 	void		*pr_linux;			/* Linux ABI emulation */
 	int		 pr_securelevel;		/* jail securelevel */
 	struct varsymset pr_varsymset;			/* jail varsyms */
+
+	struct sysctl_ctx_list pr_sysctl_ctx;
+	struct sysctl_oid *pr_sysctl_tree;
+
+	int8_t		pr_set_hostname_allowed;
+	int8_t		pr_socket_unixiproute_only;
+	int8_t		pr_sysvipc_allowed;
+	int8_t		pr_chflags_allowed;
+	int8_t		pr_allow_raw_sockets;
 };
 
 /*
@@ -121,6 +134,8 @@ struct sockaddr *
 int	prison_priv_check(struct ucred *cred, int priv);
 int	prison_remote_ip(struct thread *td, struct sockaddr *ip);
 int	prison_replace_wildcards(struct thread *td, struct sockaddr *ip);
+int	prison_sysctl_create(struct prison *);
+int	prison_sysctl_done(struct prison *);
 
 /*
  * Return 1 if the passed credential is in a jail, otherwise 0.
-- 
2.22.0

