[PATCH v26 22/25] Audit: Add new record for multiple process LSM attributes

Casey Schaufler casey at schaufler-ca.com
Tue May 25 16:26:45 UTC 2021


On 5/21/2021 1:19 PM, Paul Moore wrote:
> On Thu, May 13, 2021 at 4:32 PM Casey Schaufler <casey at schaufler-ca.com> wrote:
>> Create a new audit record type to contain the subject information
>> when there are multiple security modules that require such data.
>> This record is linked with the same timestamp and serial number
>> using the audit_alloc_local() mechanism.
> The record is linked with the other associated records into a single
> event, it doesn't matter if it gets the timestamp/serial from
> audit_alloc_local() or an existing audit event, e.g. ongoing syscall.
>
>> The record is produced only in cases where there is more than one
>> security module with a process "context".
>> In cases where this record is produced the subj= fields of
>> other records in the audit event will be set to "subj=?".
>>
>> An example of the MAC_TASK_CONTEXTS (1420) record is:
>>
>>         type=UNKNOWN[1420]
>>         msg=audit(1600880931.832:113)
>>         subj_apparmor==unconfined
> It should be just a single "=" in the line above.
>
>>         subj_smack=_
>>
>> There will be a subj_$LSM= entry for each security module
>> LSM that supports the secid_to_secctx and secctx_to_secid
>> hooks. The BPF security module implements secid/secctx
>> translation hooks, so it has to be considered to provide a
>> secctx even though it may not actually do so.
>>
>> Signed-off-by: Casey Schaufler <casey at schaufler-ca.com>
>> To: paul at paul-moore.com
>> To: linux-audit at redhat.com
>> To: rgb at redhat.com
>> Cc: netdev at vger.kernel.org
>> ---
>>  drivers/android/binder.c                |  2 +-
>>  include/linux/audit.h                   | 24 ++++++++
>>  include/linux/security.h                | 16 ++++-
>>  include/net/netlabel.h                  |  3 +-
>>  include/net/scm.h                       |  2 +-
>>  include/net/xfrm.h                      | 13 +++-
>>  include/uapi/linux/audit.h              |  1 +
>>  kernel/audit.c                          | 80 ++++++++++++++++++-------
>>  kernel/audit.h                          |  3 +
>>  kernel/auditfilter.c                    |  6 +-
>>  kernel/auditsc.c                        | 75 ++++++++++++++++++++---
>>  net/ipv4/ip_sockglue.c                  |  2 +-
>>  net/netfilter/nf_conntrack_netlink.c    |  4 +-
>>  net/netfilter/nf_conntrack_standalone.c |  2 +-
>>  net/netfilter/nfnetlink_queue.c         |  2 +-
>>  net/netlabel/netlabel_domainhash.c      |  4 +-
>>  net/netlabel/netlabel_unlabeled.c       | 24 ++++----
>>  net/netlabel/netlabel_user.c            | 20 ++++---
>>  net/netlabel/netlabel_user.h            |  6 +-
>>  net/xfrm/xfrm_policy.c                  | 10 ++--
>>  net/xfrm/xfrm_state.c                   | 20 ++++---
>>  security/integrity/ima/ima_api.c        |  7 ++-
>>  security/integrity/integrity_audit.c    |  6 +-
>>  security/security.c                     | 46 +++++++++-----
>>  security/smack/smackfs.c                |  3 +-
>>  25 files changed, 274 insertions(+), 107 deletions(-)
> ...
>
>> diff --git a/include/linux/audit.h b/include/linux/audit.h
>> index 97cd7471e572..229cd71fbf09 100644
>> --- a/include/linux/audit.h
>> +++ b/include/linux/audit.h
>> @@ -386,6 +395,19 @@ static inline void audit_ptrace(struct task_struct *t)
>>                 __audit_ptrace(t);
>>  }
>>
>> +static inline struct audit_context *audit_alloc_for_lsm(gfp_t gfp)
>> +{
>> +       struct audit_context *context = audit_context();
>> +
>> +       if (context)
>> +               return context;
>> +
>> +       if (lsm_multiple_contexts())
>> +               return audit_alloc_local(gfp);
>> +
>> +       return NULL;
>> +}
> See my other comments, but this seems wrong at face value.  The
> additional LSM record should happen as part of the existing audit log
> functions.
>
>> diff --git a/include/linux/security.h b/include/linux/security.h
>> index 0129400ff6e9..ddab456e93d3 100644
>> --- a/include/linux/security.h
>> +++ b/include/linux/security.h
>> @@ -182,6 +182,8 @@ struct lsmblob {
>>  #define LSMBLOB_INVALID                -1      /* Not a valid LSM slot number */
>>  #define LSMBLOB_NEEDED         -2      /* Slot requested on initialization */
>>  #define LSMBLOB_NOT_NEEDED     -3      /* Slot not requested */
>> +#define LSMBLOB_DISPLAY                -4      /* Use the "display" slot */
>> +#define LSMBLOB_FIRST          -5      /* Use the default "display" slot */
>>
>>  /**
>>   * lsmblob_init - initialize an lsmblob structure
>> @@ -248,6 +250,15 @@ static inline u32 lsmblob_value(const struct lsmblob *blob)
>>         return 0;
>>  }
>>
>> +static inline bool lsm_multiple_contexts(void)
>> +{
>> +#ifdef CONFIG_SECURITY
>> +       return lsm_slot_to_name(1) != NULL;
>> +#else
>> +       return false;
>> +#endif
>> +}
>> +
>>  /* These functions are in security/commoncap.c */
>>  extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
>>                        int cap, unsigned int opts);
>> @@ -578,7 +589,8 @@ int security_setprocattr(const char *lsm, const char *name, void *value,
>>                          size_t size);
>>  int security_netlink_send(struct sock *sk, struct sk_buff *skb);
>>  int security_ismaclabel(const char *name);
>> -int security_secid_to_secctx(struct lsmblob *blob, struct lsmcontext *cp);
>> +int security_secid_to_secctx(struct lsmblob *blob, struct lsmcontext *cp,
>> +                            int display);
>>  int security_secctx_to_secid(const char *secdata, u32 seclen,
>>                              struct lsmblob *blob);
>>  void security_release_secctx(struct lsmcontext *cp);
>> @@ -1433,7 +1445,7 @@ static inline int security_ismaclabel(const char *name)
>>  }
>>
>>  static inline int security_secid_to_secctx(struct lsmblob *blob,
>> -                                          struct lsmcontext *cp)
>> +                                          struct lsmcontext *cp, int display)
>>  {
>>         return -EOPNOTSUPP;
>>  }
>> diff --git a/include/net/netlabel.h b/include/net/netlabel.h
>> index 73fc25b4042b..9bc1f969a25d 100644
>> --- a/include/net/netlabel.h
>> +++ b/include/net/netlabel.h
>> @@ -97,7 +97,8 @@ struct calipso_doi;
>>
>>  /* NetLabel audit information */
>>  struct netlbl_audit {
>> -       u32 secid;
>> +       struct audit_context *localcontext;
>> +       struct lsmblob lsmdata;
>>         kuid_t loginuid;
>>         unsigned int sessionid;
>>  };
>> diff --git a/include/net/scm.h b/include/net/scm.h
>> index b77a52f93389..f4d567d4885e 100644
>> --- a/include/net/scm.h
>> +++ b/include/net/scm.h
>> @@ -101,7 +101,7 @@ static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct sc
>>                  * and the infrastructure will know which it is.
>>                  */
>>                 lsmblob_init(&lb, scm->secid);
>> -               err = security_secid_to_secctx(&lb, &context);
>> +               err = security_secid_to_secctx(&lb, &context, LSMBLOB_DISPLAY);
>>
>>                 if (!err) {
>>                         put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, context.len,
>> diff --git a/include/net/xfrm.h b/include/net/xfrm.h
>> index c58a6d4eb610..f8ad20d34498 100644
>> --- a/include/net/xfrm.h
>> +++ b/include/net/xfrm.h
>> @@ -669,13 +669,22 @@ struct xfrm_spi_skb_cb {
>>  #define XFRM_SPI_SKB_CB(__skb) ((struct xfrm_spi_skb_cb *)&((__skb)->cb[0]))
>>
>>  #ifdef CONFIG_AUDITSYSCALL
>> -static inline struct audit_buffer *xfrm_audit_start(const char *op)
>> +static inline struct audit_buffer *xfrm_audit_start(const char *op,
>> +                                                   struct audit_context **lac)
>>  {
>> +       struct audit_context *context;
>>         struct audit_buffer *audit_buf = NULL;
>>
>>         if (audit_enabled == AUDIT_OFF)
>>                 return NULL;
>> -       audit_buf = audit_log_start(audit_context(), GFP_ATOMIC,
>> +       context = audit_context();
>> +       if (lac != NULL) {
>> +               if (lsm_multiple_contexts() && context == NULL)
>> +                       context = audit_alloc_local(GFP_ATOMIC);
>> +               *lac = context;
>> +       }
> Okay, we've got a disconnect here regarding "audit contexts" and
> "local contexts", skip down below where I attempt to explain things a
> little more but basically if there is a place that uses this pattern:
>
>   audit_log_start(audit_context(), ...);
>
> ... you don't need, or want, a "local context".  You might need a
> local context if you see the following pattern:
>
>   audit_log_start(NULL, ...);
>
> The "local context" idea is a hack and should be avoided whenever
> possible; if you have an existing audit context from a syscall, or
> something else, you *really* should use it ... or have a *really* good
> explanation as to why you can not.
>
>> +       audit_buf = audit_log_start(context, GFP_ATOMIC,
>>                                     AUDIT_MAC_IPSEC_EVENT);
>>         if (audit_buf == NULL)
>>                 return NULL;
>> diff --git a/kernel/audit.c b/kernel/audit.c
>> index 841123390d41..60c027d7759c 100644
>> --- a/kernel/audit.c
>> +++ b/kernel/audit.c
>> @@ -386,10 +386,12 @@ void audit_log_lost(const char *message)
>>  static int audit_log_config_change(char *function_name, u32 new, u32 old,
>>                                    int allow_changes)
>>  {
>> +       struct audit_context *context;
>>         struct audit_buffer *ab;
>>         int rc = 0;
>>
>> -       ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE);
>> +       context = audit_alloc_for_lsm(GFP_KERNEL);
>> +       ab = audit_log_start(context, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
> Use the existing context, don't create your own, it breaks the record
> associations in the audit event stream.
>
>>         if (unlikely(!ab))
>>                 return rc;
>>         audit_log_format(ab, "op=set %s=%u old=%u ", function_name, new, old);
>> @@ -398,7 +400,7 @@ static int audit_log_config_change(char *function_name, u32 new, u32 old,
>>         if (rc)
>>                 allow_changes = 0; /* Something weird, deny request */
>>         audit_log_format(ab, " res=%d", allow_changes);
>> -       audit_log_end(ab);
>> +       audit_log_end_local(ab, context);
> More on this below, but we really should just use audit_log_end(),
> "local contexts" are not special, the are regular audit contexts ...
> although if they are used properly (limited scope) you do need to free
> them when you are done.
>
>>         return rc;
>>  }
>>
>> @@ -1357,7 +1355,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
>>                                 if (err)
>>                                         break;
>>                         }
>> -                       audit_log_user_recv_msg(&ab, msg_type);
>> +                       lcontext = audit_alloc_for_lsm(GFP_KERNEL);
>> +                       audit_log_common_recv_msg(lcontext, &ab, msg_type);
> Same.
>
>>                         if (msg_type != AUDIT_USER_TTY) {
>>                                 /* ensure NULL termination */
>>                                 str[data_len - 1] = '\0';
>> @@ -1370,7 +1369,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
>>                                         data_len--;
>>                                 audit_log_n_untrustedstring(ab, str, data_len);
>>                         }
>> -                       audit_log_end(ab);
>> +                       audit_log_end_local(ab, lcontext);
> Same.
>
>>                 }
>>                 break;
>>         case AUDIT_ADD_RULE:
>> @@ -1378,13 +1377,14 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
>>                 if (data_len < sizeof(struct audit_rule_data))
>>                         return -EINVAL;
>>                 if (audit_enabled == AUDIT_LOCKED) {
>> -                       audit_log_common_recv_msg(audit_context(), &ab,
>> +                       lcontext = audit_alloc_for_lsm(GFP_KERNEL);
>> +                       audit_log_common_recv_msg(lcontext, &ab,
>>                                                   AUDIT_CONFIG_CHANGE);
>>                         audit_log_format(ab, " op=%s audit_enabled=%d res=0",
>>                                          msg_type == AUDIT_ADD_RULE ?
>>                                                 "add_rule" : "remove_rule",
>>                                          audit_enabled);
>> -                       audit_log_end(ab);
>> +                       audit_log_end_local(ab, lcontext);
> Same.  I'm going to stop calling these out, I think you get the idea.
>
>> @@ -2396,6 +2415,21 @@ void audit_log_end(struct audit_buffer *ab)
>>         audit_buffer_free(ab);
>>  }
>>
>> +/**
>> + * audit_log_end_local - end one audit record with local context
>> + * @ab: the audit_buffer
>> + * @context: the local context
>> + *
>> + * Emit an LSM context record if appropriate, then end the audit event
>> + * in the usual way.
>> + */
>> +void audit_log_end_local(struct audit_buffer *ab, struct audit_context *context)
>> +{
>> +       audit_log_end(ab);
>> +       audit_log_lsm_common(context);
>> +       audit_free_local(context);
>> +}
> Eeesh, no, not this please.
>
> First, some background on audit contexts and the idea of a "local
> context" as we have been using it in the audit container ID work,
> which is where this originated.  An audit context contains a few
> things, but likely the most important for this discussion is the audit
> event timestamp and serial number (I may refer to this combo as just a
> "timestamp" in the future); this timestamp/serial is shared across all
> of the audit records that make up this audit event, linking them
> together.  A shared timestamp is what allows you to group an open()
> SYSCALL record with the PATH record that provides the file's pathname
> info.
>
> While there are some exceptions in the current code, most audit events
> occur as a result of a syscall, and their audit context in this case
> is the syscall's audit context (see the open() example above), but
> there are some cases being discussed where we have an audit event that
> does not occur as a result of a syscall but there is a need to group
> multiple audit records together in a single event.  This is where the
> "local context" comes into play, it allows us to create an audit
> context outside of a syscall and share that context across multiple
> audit records, allowing the records to share a timestamp/serial and
> grouping them together as a single audit event in the audit stream.
>
> While a function like audit_alloc_local() make sense, there really
> shouldn't be an audit_log_end_local() function, the normal
> audit_log_end() function should be used.
>
> Does that make sense?
>
>> diff --git a/kernel/audit.h b/kernel/audit.h
>> index 27ef690afd30..5ad0c6819aa8 100644
>> --- a/kernel/audit.h
>> +++ b/kernel/audit.h
>> @@ -100,6 +100,7 @@ struct audit_context {
>>         int                 dummy;      /* must be the first element */
>>         int                 in_syscall; /* 1 if task is in a syscall */
>>         bool                local;      /* local context needed */
>> +       bool                lsmdone;    /* multiple security reported */
> "lsmdone" doesn't seem consistent with the comment, how about
> "lsm_multi" or something similar?
>
>>         enum audit_state    state, current_state;
>>         unsigned int        serial;     /* serial number for record */
>>         int                 major;      /* syscall number */
>> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
>> index d4e061f95da8..55509faf5341 100644
>> --- a/kernel/auditsc.c
>> +++ b/kernel/auditsc.c
>> @@ -1013,6 +1013,13 @@ void audit_free_context(struct audit_context *context)
>>  }
>>  EXPORT_SYMBOL(audit_free_context);
>>
>> +void audit_free_local(struct audit_context *context)
>> +{
>> +       if (context && context->local)
>> +               audit_free_context(context);
>> +}
>> +EXPORT_SYMBOL(audit_free_local);
> We don't need this function, just use audit_free_context().  A "local
> context" is the same as a non-local context; what makes a context
> "local" is the scope of the audit context (local function scope vs
> syscall scope) and nothing else.
>
>> @@ -1504,6 +1512,47 @@ static void audit_log_proctitle(void)
>>         audit_log_end(ab);
>>  }
>>
>> +void audit_log_lsm_common(struct audit_context *context)
>> +{
>> +       struct audit_buffer *ab;
>> +       struct lsmcontext lsmdata;
>> +       bool sep = false;
>> +       int error;
>> +       int i;
>> +
>> +       if (!lsm_multiple_contexts() || context == NULL ||
>> +           !lsmblob_is_set(&context->lsm))
>> +               return;
>> +
>> +       ab = audit_log_start(context, GFP_ATOMIC, AUDIT_MAC_TASK_CONTEXTS);
>> +       if (!ab)
>> +               return; /* audit_panic or being filtered */
> We should be consistent with our use of audit_panic() when we bail on
> error; we use it below, but not here - why?
>
>> +       for (i = 0; i < LSMBLOB_ENTRIES; i++) {
>> +               if (context->lsm.secid[i] == 0)
>> +                       continue;
>> +               error = security_secid_to_secctx(&context->lsm, &lsmdata, i);
>> +               if (error && error != -EINVAL) {
>> +                       audit_panic("error in audit_log_lsm");
>> +                       return;
>> +               }
>> +
>> +               audit_log_format(ab, "%ssubj_%s=%s", sep ? " " : "",
>> +                                lsm_slot_to_name(i), lsmdata.context);
>> +               sep = true;
>> +               security_release_secctx(&lsmdata);
>> +       }
>> +       audit_log_end(ab);
>> +       context->lsmdone = true;
> Maybe I missed it, but why do we need this flag?
>
>> +}
>> +
>> +void audit_log_lsm(struct audit_context *context)
>> +{
>> +       if (!context->lsmdone)
>> +               audit_log_lsm_common(context);
>> +}
> I think I was distracted with the local context issue and I've lost
> track of the details here, perhaps it's best to fix the local context
> issue first (that should be a big change to this patch) and then we
> can take another look.
>
>
> --
> paul moore
> www.paul-moore.com



More information about the Linux-security-module-archive mailing list