From 7104f312191ea81da6faa5e08691fbbdd2cf25a3 Mon Sep 17 00:00:00 2001 From: Antonio Huete Jimenez Date: Wed, 21 Aug 2019 02:35:42 +0200 Subject: [PATCH] jail - Rework sysctl configuration variables - Jail sysctls are now jail-specific so that different jails can have different settings. Each jail will have its own subtree which can be operated directly with sysctl(8). Naming convention: jail.. - All previous sysctls are now moved to 'jail.defaults' and they are used as a template for any newly created jail. Example: # jls JID Hostname Path IPs 2 t02.local /jails/02 10.0.0.3 1 t01.local /jails/01 10.0.0.2 # sysctl jail jail.jailed: 0 jail.list: 2 t02.local /jails/02 10.0.0.3 1 t01.local /jails/01 10.0.0.2 jail.defaults.allow_raw_sockets: 0 jail.defaults.chflags_allowed: 0 jail.defaults.sysvipc_allowed: 0 jail.defaults.socket_unixiproute_only: 1 jail.defaults.set_hostname_allowed: 1 jail.1.set_hostname_allowed: 1 jail.1.socket_unixiproute_only: 1 jail.1.sysvipc_allowed: 0 jail.1.chflags_allowed: 0 jail.1.allow_raw_sockets: 0 jail.2.set_hostname_allowed: 1 jail.2.socket_unixiproute_only: 1 jail.2.sysvipc_allowed: 0 jail.2.chflags_allowed: 0 jail.2.allow_raw_sockets: 0 # sysctl jail.2.allow_raw_sockets=1 jail.2.allow_raw_sockets: 0 -> 1 # jexec 2 ping -q -c 1 10.0.0.1 PING 10.0.0.1 (10.0.0.1): 56 data bytes --- 10.0.0.1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 0.766/0.766/0.766/0.000 ms # jexec 1 ping -q -c 1 10.0.0.1 ping: socket: Operation not permitted # service jail stop Stopping jails: t01.local t02.local. # sysctl jail jail.jailed: 0 jail.defaults.allow_raw_sockets: 0 jail.defaults.chflags_allowed: 0 jail.defaults.sysvipc_allowed: 0 jail.defaults.socket_unixiproute_only: 1 jail.defaults.set_hostname_allowed: 1 --- sys/kern/kern_jail.c | 223 ++++++++++++++++++++++++++++++++++++++--- 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, 255 insertions(+), 29 deletions(-) diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 4363406700..14736181fb 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -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,171 @@ 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); + + pr->pr_sysctl_ctx = (struct sysctl_ctx_list *) kmalloc( + sizeof(struct sysctl_ctx_list), M_TEMP, M_WAITOK | M_ZERO); + + 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); + kfree(pr->pr_sysctl_ctx, M_TEMP); + 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..d6cb217e87 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -27,6 +27,10 @@ #ifndef _NET_IF_H_ #include #endif +#ifndef _SYS_SYSCTL_H_ +#include +#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.41.0