The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=4be491e1b9b3b650494208eb61511f4a45c7db3f

commit 4be491e1b9b3b650494208eb61511f4a45c7db3f
Author:     Mark Johnston <ma...@freebsd.org>
AuthorDate: 2025-09-15 17:23:50 +0000
Commit:     Mark Johnston <ma...@freebsd.org>
CommitDate: 2025-09-16 15:48:25 +0000

    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.
    
    This is a reapplication of commit 246d7e9fc23928 following a revert.
    The audit system calls must preserve the old behaviour of returning
    ENOSYS if the system call is disallowed within a jail, as some
    applications depend on that behaviour.
    
    Reviewed by:    kevans, jamie (previous version)
    MFC after:      1 week
    Sponsored by:   Modirum MDPay
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D51719
    Differential Revision:  https://reviews.freebsd.org/D52572
---
 sys/kern/kern_jail.c                   | 13 +++++++++-
 sys/security/audit/audit_syscalls.c    | 47 +++++++++++++++++++++-------------
 sys/sys/jail.h                         |  3 ++-
 usr.sbin/jail/jail.8                   | 19 +++++++++++---
 usr.sbin/jail/tests/jail_basic_test.sh | 20 +++++++++++++++
 5 files changed, 78 insertions(+), 24 deletions(-)

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

Reply via email to