diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index a75ba89d2a7e..3697d95fe0e5 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -243,6 +243,9 @@ static struct bool_flags pr_flag_allow[NBBY * NBPW] = { {"allow.unprivileged_parent_tampering", "allow.nounprivileged_parent_tampering", PR_ALLOW_UNPRIV_PARENT_TAMPER}, +#ifdef AUDIT + {"allow.setaudit", "allow.nosetaudit", PR_ALLOW_SETAUDIT}, +#endif }; static unsigned pr_allow_all = PR_ALLOW_ALL_STATIC; const size_t pr_flag_allow_size = sizeof(pr_flag_allow); @@ -4289,7 +4292,6 @@ prison_priv_check(struct ucred *cred, int priv) */ case PRIV_KTRACE: -#if 0 /* * Allow jailed processes to configure audit identity and * submit audit records (login, etc). In the future we may @@ -4298,6 +4300,11 @@ prison_priv_check(struct ucred *cred, int priv) */ case PRIV_AUDIT_GETAUDIT: case PRIV_AUDIT_SETAUDIT: + if (cred->cr_prison->pr_allow & PR_ALLOW_SETAUDIT) + return (0); + else + return (EPERM); +#if 0 case PRIV_AUDIT_SUBMIT: #endif @@ -5034,6 +5041,10 @@ SYSCTL_JAIL_PARAM(_allow, settime, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may set system time"); SYSCTL_JAIL_PARAM(_allow, routing, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may modify routing table"); +#ifdef AUDIT +SYSCTL_JAIL_PARAM(_allow, setaudit, CTLTYPE_INT | CTLFLAG_RW, + "B", "Jail may set and get audit session state"); +#endif SYSCTL_JAIL_PARAM_SUBNODE(allow, mount, "Jail mount/unmount permission flags"); SYSCTL_JAIL_PARAM(_allow_mount, , CTLTYPE_INT | CTLFLAG_RW, diff --git a/sys/security/audit/audit_syscalls.c b/sys/security/audit/audit_syscalls.c index 40b2fb3d1c9f..f50c627b7515 100644 --- a/sys/security/audit/audit_syscalls.c +++ b/sys/security/audit/audit_syscalls.c @@ -54,6 +54,29 @@ #ifdef AUDIT +static int +audit_priv_check_cred(struct ucred *cred, int priv) +{ + int error; + + error = priv_check_cred(cred, priv); + if (error == EPERM && jailed(cred)) { + /* + * The audit system calls historically returned ENOSYS when + * invoked from within a jail, and some userspace applications + * handle that case specially. Thus, convert the error here. + */ + error = ENOSYS; + } + return (error); +} + +static int +audit_priv_check(struct thread *td, int priv) +{ + return (audit_priv_check_cred(td->td_ucred, priv)); +} + /* * System call to allow a user space application to submit a BSM audit record * to the kernel for inclusion in the audit log. This function does little @@ -592,9 +615,7 @@ sys_getauid(struct thread *td, struct getauid_args *uap) { int error; - if (jailed(td->td_ucred)) - return (ENOSYS); - error = priv_check(td, PRIV_AUDIT_GETAUDIT); + error = audit_priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); return (copyout(&td->td_ucred->cr_audit.ai_auid, uap->auid, @@ -609,8 +630,6 @@ sys_setauid(struct thread *td, struct setauid_args *uap) au_id_t id; int error; - if (jailed(td->td_ucred)) - return (ENOSYS); error = copyin(uap->auid, &id, sizeof(id)); if (error) return (error); @@ -624,7 +643,7 @@ sys_setauid(struct thread *td, struct setauid_args *uap) if (error) goto fail; #endif - error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); + error = audit_priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); if (error) goto fail; newcred->cr_audit.ai_auid = id; @@ -650,9 +669,7 @@ sys_getaudit(struct thread *td, struct getaudit_args *uap) int error; cred = td->td_ucred; - if (jailed(cred)) - return (ENOSYS); - error = priv_check(td, PRIV_AUDIT_GETAUDIT); + error = audit_priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); if (cred->cr_audit.ai_termid.at_type == AU_IPv6) @@ -674,8 +691,6 @@ sys_setaudit(struct thread *td, struct setaudit_args *uap) struct auditinfo ai; int error; - if (jailed(td->td_ucred)) - return (ENOSYS); error = copyin(uap->auditinfo, &ai, sizeof(ai)); if (error) return (error); @@ -689,7 +704,7 @@ sys_setaudit(struct thread *td, struct setaudit_args *uap) if (error) goto fail; #endif - error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); + error = audit_priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); if (error) goto fail; bzero(&newcred->cr_audit, sizeof(newcred->cr_audit)); @@ -715,11 +730,9 @@ sys_getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) { int error; - if (jailed(td->td_ucred)) - return (ENOSYS); if (uap->length < sizeof(*uap->auditinfo_addr)) return (EOVERFLOW); - error = priv_check(td, PRIV_AUDIT_GETAUDIT); + error = audit_priv_check(td, PRIV_AUDIT_GETAUDIT); if (error) return (error); return (copyout(&td->td_ucred->cr_audit, uap->auditinfo_addr, @@ -734,8 +747,6 @@ sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) struct auditinfo_addr aia; int error; - if (jailed(td->td_ucred)) - return (ENOSYS); error = copyin(uap->auditinfo_addr, &aia, sizeof(aia)); if (error) return (error); @@ -752,7 +763,7 @@ sys_setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) if (error) goto fail; #endif - error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); + error = audit_priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT); if (error) goto fail; newcred->cr_audit = aia; diff --git a/sys/sys/jail.h b/sys/sys/jail.h index e12e8c3178c9..e6a13e6719dd 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -271,6 +271,7 @@ struct prison_racct { #define PR_ALLOW_SETTIME 0x00100000 #define PR_ALLOW_ROUTING 0x00200000 #define PR_ALLOW_UNPRIV_PARENT_TAMPER 0x00400000 +#define PR_ALLOW_SETAUDIT 0x00800000 /* * PR_ALLOW_PRISON0 are the allow flags that we apply by default to prison0, @@ -278,7 +279,7 @@ struct prison_racct { * build time. PR_ALLOW_ALL_STATIC should contain any bit above that we expect * to be used on the system, while PR_ALLOW_PRISON0 will be some subset of that. */ -#define PR_ALLOW_ALL_STATIC 0x007f87ff +#define PR_ALLOW_ALL_STATIC 0x00ff87ff #define PR_ALLOW_PRISON0 \ (PR_ALLOW_ALL_STATIC & ~(PR_ALLOW_UNPRIV_PARENT_TAMPER)) diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index 421aa9babb4c..d44b7f66a64e 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd August 7, 2025 +.Dd September 15, 2025 .Dt JAIL 8 .Os .Sh NAME @@ -702,15 +702,15 @@ The super-user will be disabled automatically if its parent system has it disabled. The super-user is enabled by default. .It Va allow.extattr -Allow privileged process in the jail to manipulate filesystem extended +Allow privileged processes in the jail to manipulate filesystem extended attributes in the system namespace. .It Va allow.adjtime -Allow privileged process in the jail to slowly adjusting global operating system +Allow privileged processes in the jail to slowly adjusting global operating system time. For example through utilities like .Xr ntpd 8 . .It Va allow.settime -Allow privileged process in the jail to set global operating system data +Allow privileged processes in the jail to set global operating system data and time. For example through utilities like .Xr date 1 . @@ -719,6 +719,17 @@ This permission includes also .It Va allow.routing Allow privileged process in the non-VNET jail to modify the system routing table. +.It Va allow.setaudit +Allow privileged processes in the jail to set +.Xr audit 4 +session state using +.Xr setaudit 2 +and related system calls. +This is useful, for example, for allowing a jailed +.Xr sshd 8 +to set the audit user ID for an authenticated session. +However, it gives jailed processes the ability to modify or disable audit +session state, so should be configured with care. .El .El .Pp diff --git a/usr.sbin/jail/tests/jail_basic_test.sh b/usr.sbin/jail/tests/jail_basic_test.sh index 6802da7b049a..c781eed78756 100755 --- a/usr.sbin/jail/tests/jail_basic_test.sh +++ b/usr.sbin/jail/tests/jail_basic_test.sh @@ -306,6 +306,25 @@ param_consistency_cleanup() fi } +atf_test_case "setaudit" +setaudit_head() +{ + atf_set descr 'Test that setaudit works in a jail when configured with allow.setaudit' + atf_set require.user root + atf_set require.progs setaudit +} + +setaudit_body() +{ + # Try to modify the audit mask within a jail without + # allow.setaudit configured. + atf_check -s not-exit:0 -o empty -e not-empty jail -c name=setaudit_jail \ + command=setaudit -m fr ls / + # The command should succeed if allow.setaudit is configured. + atf_check -s exit:0 -o ignore -e empty jail -c name=setaudit_jail \ + allow.setaudit command=setaudit -m fr ls / +} + atf_init_test_cases() { atf_add_test_case "basic" @@ -314,4 +333,5 @@ atf_init_test_cases() atf_add_test_case "commands" atf_add_test_case "jid_name_set" atf_add_test_case "param_consistency" + atf_add_test_case "setaudit" }