diff --git a/share/man/man9/sysctl.9 b/share/man/man9/sysctl.9 --- a/share/man/man9/sysctl.9 +++ b/share/man/man9/sysctl.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 1, 2020 +.Dd October 24, 2022 .Dt SYSCTL 9 .Os .Sh NAME @@ -893,7 +893,20 @@ A process in capability mode can write to this sysctl. .It Dv CTLFLAG_SECURE This sysctl can be written to only if the effective securelevel of the -process is \[<=] 0. +system is \[<=] 0. +.It Dv CTLFLAG_KDB_SECURE +This sysctl allows users to control settings related to KDB and +.Xr ddb 4 . +To ensure that they cannot be abused to lower the effective securelevel +of the system, this flag ensures that they can only be written to if at +least one of the following two conditions is met: the securelevel is +\[<=] 0, or a +.Xr mac 9 +policy explicitly permits it. +For example, the +.Xr mac_ddb 4 +limits the possible operations of KDB such that it is safe to enter +DDB even with a raised securelevel. .It Dv CTLFLAG_PRISON This sysctl can be written to by processes in .Xr jail 2 . diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c --- a/sys/kern/kern_shutdown.c +++ b/sys/kern/kern_shutdown.c @@ -129,17 +129,17 @@ int debugger_on_panic = 1; #endif SYSCTL_INT(_debug, OID_AUTO, debugger_on_panic, - CTLFLAG_RWTUN | CTLFLAG_SECURE, + CTLFLAG_RWTUN | CTLFLAG_KDB_SECURE, &debugger_on_panic, 0, "Run debugger on kernel panic"); static bool debugger_on_recursive_panic = false; SYSCTL_BOOL(_debug, OID_AUTO, debugger_on_recursive_panic, - CTLFLAG_RWTUN | CTLFLAG_SECURE, + CTLFLAG_RWTUN | CTLFLAG_KDB_SECURE, &debugger_on_recursive_panic, 0, "Run debugger on recursive kernel panic"); int debugger_on_trap = 0; SYSCTL_INT(_debug, OID_AUTO, debugger_on_trap, - CTLFLAG_RWTUN | CTLFLAG_SECURE, + CTLFLAG_RWTUN | CTLFLAG_KDB_SECURE, &debugger_on_trap, 0, "Run debugger on kernel trap before panic"); #ifdef KDB_TRACE @@ -150,7 +150,7 @@ static bool trace_all_panics = false; #endif SYSCTL_INT(_debug, OID_AUTO, trace_on_panic, - CTLFLAG_RWTUN | CTLFLAG_SECURE, + CTLFLAG_RWTUN | CTLFLAG_KDB_SECURE, &trace_on_panic, 0, "Print stack trace on kernel panic"); SYSCTL_BOOL(_debug, OID_AUTO, trace_all_panics, CTLFLAG_RWTUN, &trace_all_panics, 0, "Print stack traces on secondary kernel panics"); diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -42,6 +42,7 @@ #include "opt_capsicum.h" #include "opt_ddb.h" +#include "opt_kdb.h" #include "opt_ktrace.h" #include "opt_sysctl.h" @@ -2247,11 +2248,24 @@ #endif /* Is this sysctl sensitive to securelevels? */ - if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) { - lvl = (oid->oid_kind & CTLMASK_SECURE) >> CTLSHIFT_SECURE; - error = securelevel_gt(req->td->td_ucred, lvl); - if (error) - goto out; + if (req->newptr) { + if (oid->oid_kind & CTLFLAG_SECURE) { + lvl = (oid->oid_kind & CTLMASK_SECURE) >> + CTLSHIFT_SECURE; + error = securelevel_gt(req->td->td_ucred, lvl); + if (error) + goto out; + } + + if ((oid->oid_kind & CTLFLAG_KDB_SECURE) != 0) { + error = securelevel_gt(req->td->td_ucred, 0); +#ifdef MAC + if (error) + error = mac_kdb_check_sysctl(oid, req); +#endif + if (error) + goto out; + } } /* Is this sysctl writable by only privileged users? */ diff --git a/sys/kern/subr_kdb.c b/sys/kern/subr_kdb.c --- a/sys/kern/subr_kdb.c +++ b/sys/kern/subr_kdb.c @@ -103,7 +103,7 @@ "currently selected KDB backend"); SYSCTL_PROC(_debug_kdb, OID_AUTO, enter, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE | CTLFLAG_MPSAFE, NULL, 0, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_KDB_SECURE | CTLFLAG_MPSAFE, NULL, 0, kdb_sysctl_enter, "I", "set to enter the debugger"); @@ -133,11 +133,11 @@ "set to cause a stack overflow"); SYSCTL_INT(_debug_kdb, OID_AUTO, break_to_debugger, - CTLFLAG_RWTUN | CTLFLAG_SECURE, + CTLFLAG_RWTUN | CTLFLAG_KDB_SECURE, &kdb_break_to_debugger, 0, "Enable break to debugger"); SYSCTL_INT(_debug_kdb, OID_AUTO, alt_break_to_debugger, - CTLFLAG_RWTUN | CTLFLAG_SECURE, + CTLFLAG_RWTUN | CTLFLAG_KDB_SECURE, &kdb_alt_break_to_debugger, 0, "Enable alternative break to debugger"); /* diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h --- a/sys/security/mac/mac_framework.h +++ b/sys/security/mac/mac_framework.h @@ -214,6 +214,7 @@ void mac_ipq_update(struct mbuf *m, struct ipq *q); int mac_kdb_check_backend(struct kdb_dbbe *be); +int mac_kdb_check_sysctl(struct sysctl_oid *oid, struct sysctl_req *req); int mac_kenv_check_dump(struct ucred *cred); int mac_kenv_check_get(struct ucred *cred, char *name); diff --git a/sys/security/mac/mac_kdb.c b/sys/security/mac/mac_kdb.c --- a/sys/security/mac/mac_kdb.c +++ b/sys/security/mac/mac_kdb.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -48,6 +49,23 @@ return (error); } +int +mac_kdb_check_sysctl(struct sysctl_oid *oid, struct sysctl_req *req) +{ + int error = 0; + + if ((oid->oid_kind & CTLFLAG_KDB_SECURE) == 0) + /* We don't know what this is; be safe and block it. */ + return (EPERM); + + /* + * Unlike other policies, this one must be explicitly granted by a + * module in order to override the system securelevel setting. + */ + MAC_POLICY_GRANT_NOSLEEP(kdb_check_sysctl, oid, req); + return (error); +} + int mac_ddb_command_register(struct db_command_table *table, struct db_command *cmd) { diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h --- a/sys/security/mac/mac_policy.h +++ b/sys/security/mac/mac_policy.h @@ -260,6 +260,8 @@ struct ipq *q, struct label *qlabel); typedef int (*mpo_kdb_check_backend_t)(struct kdb_dbbe *be); +typedef int (*mpo_kdb_check_sysctl_t)(struct sysctl_oid *oid, + struct sysctl_req *req); typedef int (*mpo_kenv_check_dump_t)(struct ucred *cred); typedef int (*mpo_kenv_check_get_t)(struct ucred *cred, char *name); @@ -777,6 +779,7 @@ mpo_ipq_update_t mpo_ipq_update; mpo_kdb_check_backend_t mpo_kdb_check_backend; + mpo_kdb_check_sysctl_t mpo_kdb_check_sysctl; mpo_kenv_check_dump_t mpo_kenv_check_dump; mpo_kenv_check_get_t mpo_kenv_check_get; diff --git a/sys/security/mac_ddb/mac_ddb.c b/sys/security/mac_ddb/mac_ddb.c --- a/sys/security/mac_ddb/mac_ddb.c +++ b/sys/security/mac_ddb/mac_ddb.c @@ -358,6 +358,21 @@ return (EACCES); } +static int +mac_ddb_check_sysctl(struct sysctl_oid *oid __diagused, + struct sysctl_req *req __unused) +{ + + KASSERT((oid->oid_kind & CTLFLAG_KDB_SECURE) != 0, + ("%s: unhandled OID %p", __func__, oid)); + + /* + * It's ok to enable access to DDB since it cannot be used to modify the + * system securelevel when this policy is loaded. + */ + return (0); +} + /* * Register functions with MAC Framework policy entry points. */ @@ -369,5 +384,6 @@ .mpo_ddb_command_exec = mac_ddb_command_exec, .mpo_kdb_check_backend = mac_ddb_check_backend, + .mpo_kdb_check_sysctl = mac_ddb_check_sysctl, }; MAC_POLICY_SET(&mac_ddb_ops, mac_ddb, "MAC/DDB", 0, NULL); diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c --- a/sys/security/mac_stub/mac_stub.c +++ b/sys/security/mac_stub/mac_stub.c @@ -502,6 +502,13 @@ return (0); } +static int +stub_kdb_check_sysctl(struct sysctl_oid *oidp, struct sysctl_req *req) +{ + + return (0); +} + static int stub_kenv_check_dump(struct ucred *cred) { @@ -1756,6 +1763,7 @@ .mpo_ipq_reassemble = stub_ipq_reassemble, .mpo_kdb_check_backend = stub_kdb_check_backend, + .mpo_kdb_check_sysctl = stub_kdb_check_sysctl, .mpo_kenv_check_dump = stub_kenv_check_dump, .mpo_kenv_check_get = stub_kenv_check_get, diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c --- a/sys/security/mac_test/mac_test.c +++ b/sys/security/mac_test/mac_test.c @@ -902,6 +902,16 @@ return (0); } +COUNTER_DECL(kdb_sysctl_check); +static int +test_kdb_check_sysctl(struct sysctl_oid *oid, struct sysctl_req *req) +{ + + COUNTER_INC(kdb_sysctl_check); + + return (0); +} + COUNTER_DECL(kenv_check_dump); static int test_kenv_check_dump(struct ucred *cred) @@ -3116,6 +3126,7 @@ .mpo_ipq_update = test_ipq_update, .mpo_kdb_check_backend = test_kdb_check_backend, + .mpo_kdb_check_sysctl = test_kdb_check_sysctl, .mpo_kenv_check_dump = test_kenv_check_dump, .mpo_kenv_check_get = test_kenv_check_get, diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -96,16 +96,23 @@ #define CTLFLAG_DYING 0x00010000 /* Oid is being removed */ #define CTLFLAG_CAPRD 0x00008000 /* Can be read in capability mode */ #define CTLFLAG_CAPWR 0x00004000 /* Can be written in capability mode */ +#define CTLFLAG_CAPRW (CTLFLAG_CAPRD|CTLFLAG_CAPWR) #define CTLFLAG_STATS 0x00002000 /* Statistics, not a tuneable */ #define CTLFLAG_NOFETCH 0x00001000 /* Don't fetch tunable from getenv() */ -#define CTLFLAG_CAPRW (CTLFLAG_CAPRD|CTLFLAG_CAPWR) /* * This is transient flag to be used until all sysctl handlers are converted * to not lock Giant. * One, and only one of CTLFLAG_MPSAFE or CTLFLAG_NEEDGIANT is required * for SYSCTL_PROC and SYSCTL_NODE. */ -#define CTLFLAG_NEEDGIANT 0x00000800 /* Handler require Giant */ +#define CTLFLAG_NEEDGIANT 0x00000800 /* Handler require Giant */ +/* + * sysctls with KDB_SECURE set allow a privileged user to break into the kernel + * debugger. Since KDB can be used to lower the system's securelevel, they are + * normally subject to the same restrictions as CTLFLAG_SECURE, except that + * MAC's KDB hooks may be used to override the securelevel check. + */ +#define CTLFLAG_KDB_SECURE 0x00000400 /* Enables KDB access */ /* * Secure level. Note that CTLFLAG_SECURE == CTLFLAG_SECURE1.