jail: Optionally allow audit session state to be configured in a jail

Currently it is impossible for a privileged, jailed process to set audit
session state.  This can result in suprising audit event misattribution.
For example, suppose a user ssh'es into a jail and restarts a service;
normally, sshd sets audit state such that events generated by the SSH
session are attributed to the newly authenticated user, but in a jail,
the corresponding setaudit(2) call fails, so events are attributed to
the user who had started sshd in the jail (typically the user who had
started the jail itself by some means).

While this behaviour is reasonable, administrators might want to trust
the jailed sshd to reset audit state, such that the authenticated user
appears in audit logs.  Add a jail knob to enable this.  Add a simple
regression test.

Reviewed by:	kevans, jamie
MFC after:	1 week
Sponsored by:	Modirum MDPay
Sponsored by:	Klara, Inc.
Differential Revision:	https://reviews.freebsd.org/D51719
This commit is contained in:
Mark Johnston 2025-09-15 17:23:50 +00:00
parent cd02a8a9f8
commit 246d7e9fc2
5 changed files with 49 additions and 18 deletions

View file

@ -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,

View file

@ -592,8 +592,6 @@ 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);
if (error)
return (error);
@ -609,8 +607,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);
@ -650,8 +646,6 @@ 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);
if (error)
return (error);
@ -674,8 +668,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);
@ -715,8 +707,6 @@ 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);
@ -734,8 +724,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);

View file

@ -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))

View file

@ -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

View file

@ -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"
}