Changeset View
Standalone View
kern/kern_jail.c
Context not available. | |||||
#else | #else | ||||
.pr_flags = PR_HOST|_PR_IP_SADDRSEL, | .pr_flags = PR_HOST|_PR_IP_SADDRSEL, | ||||
#endif | #endif | ||||
.pr_allow = PR_ALLOW_ALL, | .pr_allow = PR_ALLOW_ALL_STATIC, | ||||
}; | }; | ||||
MTX_SYSINIT(prison0, &prison0.pr_mtx, "jail mutex", MTX_DEF); | MTX_SYSINIT(prison0, &prison0.pr_mtx, "jail mutex", MTX_DEF); | ||||
Context not available. | |||||
}; | }; | ||||
const size_t pr_flag_jailsys_size = sizeof(pr_flag_jailsys); | const size_t pr_flag_jailsys_size = sizeof(pr_flag_jailsys); | ||||
static struct bool_flags pr_flag_allow[] = { | /* Make this array full-size so dynamic parameters can be added. */ | ||||
static struct bool_flags pr_flag_allow[NBBY * NBPW] = { | |||||
{"allow.set_hostname", "allow.noset_hostname", PR_ALLOW_SET_HOSTNAME}, | {"allow.set_hostname", "allow.noset_hostname", PR_ALLOW_SET_HOSTNAME}, | ||||
{"allow.sysvipc", "allow.nosysvipc", PR_ALLOW_SYSVIPC}, | {"allow.sysvipc", "allow.nosysvipc", PR_ALLOW_SYSVIPC}, | ||||
{"allow.raw_sockets", "allow.noraw_sockets", PR_ALLOW_RAW_SOCKETS}, | {"allow.raw_sockets", "allow.noraw_sockets", PR_ALLOW_RAW_SOCKETS}, | ||||
jhb: Any reason to not let the compiler autocompute the size still? 'NBBY * NBPW' doesn't seem to… | |||||
Not Done Inline ActionsThe array needs to reflect the full 32 permission bits which could be dynamically set. If I don't go the dynamic pr_allow route this wouldn't be necessary, but see my comment on that. jamie: The array needs to reflect the full 32 permission bits which could be dynamically set. If I… | |||||
Context not available. | |||||
{"allow.mount", "allow.nomount", PR_ALLOW_MOUNT}, | {"allow.mount", "allow.nomount", PR_ALLOW_MOUNT}, | ||||
{"allow.quotas", "allow.noquotas", PR_ALLOW_QUOTAS}, | {"allow.quotas", "allow.noquotas", PR_ALLOW_QUOTAS}, | ||||
{"allow.socket_af", "allow.nosocket_af", PR_ALLOW_SOCKET_AF}, | {"allow.socket_af", "allow.nosocket_af", PR_ALLOW_SOCKET_AF}, | ||||
{"allow.mount.devfs", "allow.mount.nodevfs", PR_ALLOW_MOUNT_DEVFS}, | |||||
{"allow.mount.nullfs", "allow.mount.nonullfs", PR_ALLOW_MOUNT_NULLFS}, | |||||
{"allow.mount.zfs", "allow.mount.nozfs", PR_ALLOW_MOUNT_ZFS}, | |||||
{"allow.mount.procfs", "allow.mount.noprocfs", PR_ALLOW_MOUNT_PROCFS}, | |||||
{"allow.mount.tmpfs", "allow.mount.notmpfs", PR_ALLOW_MOUNT_TMPFS}, | |||||
{"allow.mount.fdescfs", "allow.mount.nofdescfs", | |||||
PR_ALLOW_MOUNT_FDESCFS}, | |||||
{"allow.mount.linprocfs", "allow.mount.nolinprocfs", | |||||
PR_ALLOW_MOUNT_LINPROCFS}, | |||||
{"allow.mount.linsysfs", "allow.mount.nolinsysfs", | |||||
PR_ALLOW_MOUNT_LINSYSFS}, | |||||
{"allow.reserved_ports", "allow.noreserved_ports", | {"allow.reserved_ports", "allow.noreserved_ports", | ||||
PR_ALLOW_RESERVED_PORTS}, | PR_ALLOW_RESERVED_PORTS}, | ||||
}; | }; | ||||
Not Done Inline ActionsThis is weird. I must admit. kib: This is weird. I must admit. | |||||
Not Done Inline ActionsYes, there's this gap now before PR_ALLOW_RESERVED_PORTS that requires this. Since I had to bump __FreeBSD_version anyway, I might as well close that gap (change the to defines past the mount points). I'm not really happy with the way these arrays are presented anyway. I may fix them up in a separate commit. jamie: Yes, there's this gap now before PR_ALLOW_RESERVED_PORTS that requires this. Since I had to… | |||||
Context not available. | |||||
/* Set permissions for top-level jails from sysctls. */ | /* Set permissions for top-level jails from sysctls. */ | ||||
if (!jailed(td->td_ucred)) { | if (!jailed(td->td_ucred)) { | ||||
for (bf = pr_flag_allow; | for (bf = pr_flag_allow; | ||||
bf < pr_flag_allow + nitems(pr_flag_allow); | bf < pr_flag_allow + nitems(pr_flag_allow) && | ||||
bf->flag != 0; | |||||
bf++) { | bf++) { | ||||
optiov[opt.uio_iovcnt].iov_base = __DECONST(char *, | optiov[opt.uio_iovcnt].iov_base = __DECONST(char *, | ||||
(jail_default_allow & bf->flag) | (jail_default_allow & bf->flag) | ||||
Context not available. | |||||
pr_allow = ch_allow = 0; | pr_allow = ch_allow = 0; | ||||
for (bf = pr_flag_allow; | for (bf = pr_flag_allow; | ||||
bf < pr_flag_allow + nitems(pr_flag_allow); | bf < pr_flag_allow + nitems(pr_flag_allow) && bf->flag != 0; | ||||
bf++) { | bf++) { | ||||
vfs_flagopt(opts, bf->name, &pr_allow, bf->flag); | vfs_flagopt(opts, bf->name, &pr_allow, bf->flag); | ||||
vfs_flagopt(opts, bf->noname, &ch_allow, bf->flag); | vfs_flagopt(opts, bf->noname, &ch_allow, bf->flag); | ||||
Context not available. | |||||
goto done_deref; | goto done_deref; | ||||
} | } | ||||
for (bf = pr_flag_allow; | for (bf = pr_flag_allow; | ||||
bf < pr_flag_allow + nitems(pr_flag_allow); | bf < pr_flag_allow + nitems(pr_flag_allow) && bf->flag != 0; | ||||
bf++) { | bf++) { | ||||
i = (pr->pr_allow & bf->flag) ? 1 : 0; | i = (pr->pr_allow & bf->flag) ? 1 : 0; | ||||
error = vfs_setopt(opts, bf->name, &i, sizeof(i)); | error = vfs_setopt(opts, bf->name, &i, sizeof(i)); | ||||
Context not available. | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
NULL, PR_ALLOW_MOUNT, sysctl_jail_default_allow, "I", | NULL, PR_ALLOW_MOUNT, sysctl_jail_default_allow, "I", | ||||
"Processes in jail can mount/unmount jail-friendly file systems (deprecated)"); | "Processes in jail can mount/unmount jail-friendly file systems (deprecated)"); | ||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_devfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_DEVFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the devfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_fdescfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_FDESCFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the fdescfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_nullfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_NULLFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the nullfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_procfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_PROCFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the procfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_linprocfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_LINPROCFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the linprocfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_linsysfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_LINSYSFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the linsysfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_tmpfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_TMPFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the tmpfs file system (deprecated)"); | |||||
SYSCTL_PROC(_security_jail, OID_AUTO, mount_zfs_allowed, | |||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, PR_ALLOW_MOUNT_ZFS, sysctl_jail_default_allow, "I", | |||||
"Processes in jail can mount the zfs file system (deprecated)"); | |||||
static int | static int | ||||
sysctl_jail_default_level(SYSCTL_HANDLER_ARGS) | sysctl_jail_default_level(SYSCTL_HANDLER_ARGS) | ||||
Context not available. | |||||
SYSCTL_JAIL_PARAM_SUBNODE(allow, mount, "Jail mount/unmount permission flags"); | SYSCTL_JAIL_PARAM_SUBNODE(allow, mount, "Jail mount/unmount permission flags"); | ||||
SYSCTL_JAIL_PARAM(_allow_mount, , CTLTYPE_INT | CTLFLAG_RW, | SYSCTL_JAIL_PARAM(_allow_mount, , CTLTYPE_INT | CTLFLAG_RW, | ||||
"B", "Jail may mount/unmount jail-friendly file systems in general"); | "B", "Jail may mount/unmount jail-friendly file systems in general"); | ||||
SYSCTL_JAIL_PARAM(_allow_mount, devfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the devfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, fdescfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the fdescfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, nullfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the nullfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, procfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the procfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, linprocfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the linprocfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, linsysfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the linsysfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, tmpfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the tmpfs file system"); | |||||
SYSCTL_JAIL_PARAM(_allow_mount, zfs, CTLTYPE_INT | CTLFLAG_RW, | |||||
"B", "Jail may mount the zfs file system"); | |||||
/* | |||||
* The VFS system will register jail-aware filesystems here. They each get | |||||
* a parameter allow.mount.xxxfs and a flag to check when a jailed user | |||||
* attempts to mount. | |||||
*/ | |||||
void | |||||
prison_add_vfs(struct vfsconf *vfsp) | |||||
{ | |||||
char *allow_name, *allow_noname, *mount_allowed; | |||||
struct bool_flags *bf; | |||||
#ifndef NO_SYSCTL_DESCR | |||||
char *descr; | |||||
#endif | |||||
unsigned allow_flag; | |||||
if (asprintf(&allow_name, M_PRISON, "allow.mount.%s", vfsp->vfc_name) < | |||||
0 || asprintf(&allow_noname, M_PRISON, "allow.mount.no%s", | |||||
vfsp->vfc_name) < 0) { | |||||
Not Done Inline ActionsIf NO_SYSCTL_DESCR is defined, the descr variable is not. Then you should get the compilation error. kib: If NO_SYSCTL_DESCR is defined, the descr variable is not. Then you should get the compilation… | |||||
Not Done Inline ActionsThe SYSCTL_ADD_PROC macro disregards descr when NO_SYSCTL_DESCR is defined, so it actually compiles without a problem. jamie: The SYSCTL_ADD_PROC macro disregards descr when NO_SYSCTL_DESCR is defined, so it actually… | |||||
free(allow_name, M_PRISON); | |||||
return; | |||||
} | |||||
/* | |||||
* See if this parameter has already beed added, i.e. if the filesystem | |||||
* was previously loaded/unloaded. | |||||
*/ | |||||
mtx_lock(&prison0.pr_mtx); | |||||
for (bf = pr_flag_allow; | |||||
bf < pr_flag_allow + nitems(pr_flag_allow) && bf->flag != 0; | |||||
bf++) { | |||||
if (strcmp(bf->name, allow_name) == 0) { | |||||
vfsp->vfc_prison_flag = bf->flag; | |||||
goto no_add; | |||||
} | |||||
} | |||||
/* | |||||
* Find a free bit in prison0's pr_allow, failing if there are none | |||||
* (which shouldn't happen as long as we keep track of how many | |||||
* filesystems are jail-aware). | |||||
*/ | |||||
for (allow_flag = 1;; allow_flag <<= 1) { | |||||
if (allow_flag == 0) | |||||
goto no_add; | |||||
if ((prison0.pr_allow & allow_flag) == 0) | |||||
break; | |||||
} | |||||
/* | |||||
* Note the parameter in the next open slot in pr_flag_allow. | |||||
* Set the flag last so code that checks pr_flag_allow can do so | |||||
* without locking. | |||||
*/ | |||||
for (bf = pr_flag_allow; bf->flag != 0; bf++) | |||||
if (bf == pr_flag_allow + nitems(pr_flag_allow)) { | |||||
/* This should never happen, but is not fatal. */ | |||||
goto no_add; | |||||
Not Done Inline Actions/* should be on the separate line. Also, there should be a blank line before multi-line comment. kib: /* should be on the separate line. Also, there should be a blank line before multi-line… | |||||
Not Done Inline ActionsOops - I let my own coding style slip in when I wasn't paying attention. I'll fix that. jamie: Oops - I let my own coding style slip in when I wasn't paying attention. I'll fix that. | |||||
} | |||||
prison0.pr_allow |= allow_flag; | |||||
bf->name = allow_name; | |||||
bf->noname = allow_noname; | |||||
bf->flag = allow_flag; | |||||
vfsp->vfc_prison_flag = allow_flag; | |||||
mtx_unlock(&prison0.pr_mtx); | |||||
/* | |||||
* Create sysctls for the paramter, and the back-compat global | |||||
* permission. | |||||
*/ | |||||
#ifndef NO_SYSCTL_DESCR | |||||
(void)asprintf(&descr, M_TEMP, "Jail may mount the %s file system", | |||||
vfsp->vfc_name); | |||||
#endif | |||||
(void)SYSCTL_ADD_PROC(NULL, | |||||
SYSCTL_CHILDREN(&sysctl___security_jail_param_allow_mount), | |||||
OID_AUTO, vfsp->vfc_name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, 0, sysctl_jail_param, "B", descr); | |||||
#ifndef NO_SYSCTL_DESCR | |||||
free(descr, M_TEMP); | |||||
#endif | |||||
if (asprintf(&mount_allowed, M_TEMP, "mount_%s_allowed", | |||||
vfsp->vfc_name) >= 0) { | |||||
#ifndef NO_SYSCTL_DESCR | |||||
(void)asprintf(&descr, M_TEMP, | |||||
"Processes in jail can mount the %s file system (deprecated)", | |||||
vfsp->vfc_name); | |||||
#endif | |||||
(void)SYSCTL_ADD_PROC(NULL, | |||||
SYSCTL_CHILDREN(&sysctl___security_jail), OID_AUTO, | |||||
mount_allowed, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, | |||||
NULL, allow_flag, sysctl_jail_default_allow, "I", descr); | |||||
#ifndef NO_SYSCTL_DESCR | |||||
free(descr, M_TEMP); | |||||
#endif | |||||
free(mount_allowed, M_TEMP); | |||||
} | |||||
return; | |||||
no_add: | |||||
Not Done Inline ActionsWhy not use asprintf(9) instead of manually reimplement it and calculating the string lengths ? kib: Why not use asprintf(9) instead of manually reimplement it and calculating the string lengths ? | |||||
Not Done Inline ActionsFor whatever reason, asprintf uses M_NOWAIT, while strdup uses M_WAITOK. Not that running out of memory for such a small allocation is at all likely, but I would want to handle failure (probably just return, same as no available bit). asprintf may also be the better choice than having these hand-sized allowstr and global_allowstr arrays. jamie: For whatever reason, asprintf uses M_NOWAIT, while strdup uses M_WAITOK. Not that running out… | |||||
mtx_unlock(&prison0.pr_mtx); | |||||
free(allow_name, M_PRISON); | |||||
free(allow_noname, M_PRISON); | |||||
} | |||||
#ifdef RACCT | #ifdef RACCT | ||||
void | void | ||||
prison_racct_foreach(void (*callback)(struct racct *racct, | prison_racct_foreach(void (*callback)(struct racct *racct, | ||||
Not Done Inline ActionsI would just store the bit number or bit mask for the pr_allow_mount in a member of struct vfsconf, allocated on the fs module load (you already do this). Then this function becomes trivial. kib: I would just store the bit number or bit mask for the pr_allow_mount in a member of struct… | |||||
Not Done Inline ActionsYeah, jhb preferred it that way too. I could do that (and bump __FreeBSD_version), and the current version becomes the kind of thing that would go into an MFC. jamie: Yeah, jhb preferred it that way too. I could do that (and bump __FreeBSD_version), and the… | |||||
Not Done Inline ActionsThis verbosity should be avoided if NO_SYSCTL_DESCR kernel option is configured. kib: This verbosity should be avoided if NO_SYSCTL_DESCR kernel option is configured. | |||||
Not Done Inline ActionsIndeed - I didn't know about that option. I'll put something in. jamie: Indeed - I didn't know about that option. I'll put something in. | |||||
Not Done Inline ActionsThis one too. kib: This one too. | |||||
Context not available. | |||||
} | } | ||||
db_printf(" allow = 0x%x", pr->pr_allow); | db_printf(" allow = 0x%x", pr->pr_allow); | ||||
for (bf = pr_flag_allow; | for (bf = pr_flag_allow; | ||||
bf < pr_flag_allow + nitems(pr_flag_allow); | bf < pr_flag_allow + nitems(pr_flag_allow) && bf->flag != 0; | ||||
bf++) | bf++) | ||||
if (pr->pr_allow & bf->flag) | if (pr->pr_allow & bf->flag) | ||||
db_printf(" %s", bf->name); | db_printf(" %s", bf->name); | ||||
Context not available. |
Any reason to not let the compiler autocompute the size still? 'NBBY * NBPW' doesn't seem to make sense for setting the number of pointers in the array either.