Restrict visibility of per-dataset kstats inside FreeBSD jails

When inside a jail, visibility on datasets not "jailed" to the
jail is restricted. However, it was possible to enumerate all
datasets in the pool by looking at the kstats sysctl MIB.

Only the kstats corresponding to datasets that the user has
visibility on are accessible now.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <richard.yao@alumni.stonybrook.edu>
Signed-off-by: Allan Jude <allan@klarasystems.com>
Closes #14254
This commit is contained in:
Allan Jude 2022-12-09 14:04:29 -05:00 committed by GitHub
parent 5401472cd0
commit f900279e6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/kstat.h>
#include <sys/sbuf.h>
#include <sys/zone.h>
static MALLOC_DEFINE(M_KSTAT, "kstat_data", "Kernel statistics");
@ -134,6 +135,55 @@ kstat_sysctl_string(SYSCTL_HANDLER_ARGS)
return (sysctl_handle_string(oidp, val, len, req));
}
static int
kstat_sysctl_dataset(SYSCTL_HANDLER_ARGS)
{
kstat_t *ksp = arg1;
kstat_named_t *ksent;
kstat_named_t *ksent_ds;
uint64_t val;
char *ds_name;
uint32_t ds_len = 0;
ksent_ds = ksent = ksp->ks_data;
ds_name = KSTAT_NAMED_STR_PTR(ksent_ds);
ds_len = KSTAT_NAMED_STR_BUFLEN(ksent_ds);
ds_name[ds_len-1] = '\0';
if (!zone_dataset_visible(ds_name, NULL)) {
return (EPERM);
}
/* Select the correct element */
ksent += arg2;
/* Update the aggsums before reading */
(void) ksp->ks_update(ksp, KSTAT_READ);
val = ksent->value.ui64;
return (sysctl_handle_64(oidp, &val, 0, req));
}
static int
kstat_sysctl_dataset_string(SYSCTL_HANDLER_ARGS)
{
kstat_t *ksp = arg1;
kstat_named_t *ksent = ksp->ks_data;
char *val;
uint32_t len = 0;
/* Select the correct element */
ksent += arg2;
val = KSTAT_NAMED_STR_PTR(ksent);
len = KSTAT_NAMED_STR_BUFLEN(ksent);
val[len-1] = '\0';
if (!zone_dataset_visible(val, NULL)) {
return (EPERM);
}
return (sysctl_handle_string(oidp, val, len, req));
}
static int
kstat_sysctl_io(SYSCTL_HANDLER_ARGS)
{
@ -422,11 +472,20 @@ kstat_install_named(kstat_t *ksp)
ksp, i, kstat_sysctl, "Q", namelast);
break;
case KSTAT_DATA_UINT64:
if (strcmp(ksp->ks_class, "dataset") == 0) {
SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
SYSCTL_CHILDREN(ksp->ks_sysctl_root),
OID_AUTO, namelast,
CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
ksp, i, kstat_sysctl_dataset, "QU",
namelast);
} else {
SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
SYSCTL_CHILDREN(ksp->ks_sysctl_root),
OID_AUTO, namelast,
CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
ksp, i, kstat_sysctl, "QU", namelast);
}
break;
case KSTAT_DATA_LONG:
SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
@ -443,11 +502,21 @@ kstat_install_named(kstat_t *ksp)
ksp, i, kstat_sysctl, "LU", namelast);
break;
case KSTAT_DATA_STRING:
if (strcmp(ksp->ks_class, "dataset") == 0) {
SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
SYSCTL_CHILDREN(ksp->ks_sysctl_root),
OID_AUTO, namelast,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
ksp, i, kstat_sysctl_string, "A", namelast);
OID_AUTO, namelast, CTLTYPE_STRING |
CTLFLAG_RD | CTLFLAG_MPSAFE,
ksp, i, kstat_sysctl_dataset_string, "A",
namelast);
} else {
SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
SYSCTL_CHILDREN(ksp->ks_sysctl_root),
OID_AUTO, namelast, CTLTYPE_STRING |
CTLFLAG_RD | CTLFLAG_MPSAFE,
ksp, i, kstat_sysctl_string, "A",
namelast);
}
break;
default:
panic("unsupported type: %d", typelast);