On Mon, Jan 31, 2022 at 6:29 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> On 2022-01-31 17:02, Paul Moore wrote:
> > On Wed, Jan 26, 2022 at 8:52 AM Richard Guy Briggs <rgb@redhat.com> wrote:
> > > On 2022-01-25 22:24, Richard Guy Briggs wrote:
> > > > AUDIT_TIME_* events are generated when there are syscall rules present that are
> > > > not related to time keeping.  This will produce noisy log entries that could
> > > > flood the logs and hide events we really care about.
> > > >
> > > > Rather than immediately produce the AUDIT_TIME_* records, store the data in the
> > > > context and log it at syscall exit time respecting the filter rules.
> > > >
> > > > Please see https://bugzilla.redhat.com/show_bug.cgi?id=1991919
> > > >
> > > > Fixes: 7e8eda734d30 ("ntp: Audit NTP parameters adjustment")
> > > > Fixes: 2d87a0674bd6 ("timekeeping: Audit clock adjustments")
> > > > Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> > > > ---
> > > > Changelog:
> > > > v2:
> > > > - rename __audit_ntp_log_ to audit_log_ntp
> > > > - pre-check ntp before storing
> > > > - move tk out of the context union and move ntp logging to the bottom of audit_show_special()
> > > > - restructure logging of ntp to use ab and allocate more only if more
> > > > - add Fixes lines
> > > > v3
> > > > - move tk into union
> > > > - rename audit_log_ntp() to audit_log_time() and add tk
> > > > - key off both AUDIT_TIME_* but favour NTP
> > > >
> > > >  kernel/audit.h   |  4 +++
> > > >  kernel/auditsc.c | 86 +++++++++++++++++++++++++++++++++++++-----------
> > > >  2 files changed, 70 insertions(+), 20 deletions(-)
> >
> > ...
> >
> > > > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > > > index b517947bfa48..9c6c55a81fdb 100644
> > > > --- a/kernel/auditsc.c
> > > > +++ b/kernel/auditsc.c
> > > > @@ -1331,6 +1331,55 @@ static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
> > > >                        from_kuid(&init_user_ns, name->fcap.rootid));
> > > >  }
> > > >
> > > > +void audit_log_time(struct audit_context *context, struct audit_buffer **ab)
> > > > +{
> > > > +     const struct audit_ntp_data *ntp = &context->time.ntp_data;
> > > > +     const struct timespec64 *tk = &context->time.tk_injoffset;
> > > > +     const char *ntp_name[] = {
> > > > +             "offset",
> > > > +             "freq",
> > > > +             "status",
> > > > +             "tai",
> > > > +             "tick",
> > > > +             "adjust",
> > > > +     };
> > > > +     int type, first = 1;
> > > > +
> > > > +     if (context->type == AUDIT_TIME_INJOFFSET)
> > > > +             goto tk;
> > > > +
> > > > +     /* use up allocated ab from show_special before new one */
> > > > +     for (type = 0; type < AUDIT_NTP_NVALS; type++) {
> > > > +             if (ntp->vals[type].newval != ntp->vals[type].oldval) {
> > > > +                     if (first) {
> > > > +                             first = 0;
> > > > +                     } else {
> > > > +                             audit_log_end(*ab);
> > > > +                             *ab = audit_log_start(context, GFP_KERNEL,
> > > > +                                                   AUDIT_TIME_ADJNTPVAL);
> > > > +                             if (!*ab)
> > > > +                                     return;
> > > > +                     }
> > > > +                     audit_log_format(*ab, "op=%s old=%lli new=%lli",
> > > > +                                      ntp_name[type], ntp->vals[type].oldval,
> > > > +                                      ntp->vals[type].newval);
> > > > +             }
> > > > +     }
> > > > +
> > > > +tk:
> > > > +     if (tk->tv_sec != 0 || tk->tv_nsec != 0) {
> > > > +             if (!first) {
> > > > +                     audit_log_end(*ab);
> > > > +                     *ab = audit_log_start(context, GFP_KERNEL,
> > > > +                                           AUDIT_TIME_INJOFFSET);
> > > > +                     if (!*ab)
> > > > +                             return;
> > > > +             }
> > >
> > > It occurs to me that a slight improvement could be made to move the
> > > "tk:" label here.  And better yet, change the tk condition above to:
> > >
> > >         if (!tk->tv_sec && !tk->tv_nsec)
> > >                 return;
> >
> > Honestly, I've looked at this a few times over different days trying
> > to make sure I'm not missing something, but there is a lot of
> > complexity in audit_log_time() that I don't believe is necessary.
> > What about something like below?
> >
> > [WARNING: not compiled, tested, yadda yadda]
> >
> > void audit_log_time(struct audit_context ctx, struct audit_buffer **abp)
> > {
> >   int i;
> >   int type = ctx->type;
> >   struct audit_buffer *ab = *abp;
> >   struct audit_ntp_val *ntp;
> >   const struct timespec64 *tk;
> >   const char *ntp_name[] = {
> >     "offset",
> >     "freq",
> >     "status",
> >     "tai",
> >     "tick",
> >     "adjust",
> >   };
> >
> >   do {
> >     if (type == AUDIT_TIME_ADJNTPVAL) {
> >       ntp = ctx->time.ntp_data.val;
> >       for (i = 0; i < AUDIT_NTP_NVALS; i++) {
> >         if (ntp[i].newval != ntp[i].oldval) {
> >           audit_log_format(ab,
> >             "op=%s old=%lli new=%lli",
> >             ntp_name[type],
> >             ntp[i].oldval, ntp[i].newval);
> >         }
> >       }
> >     } else {
> >       tk = &ctx->time.tk_injoffset;
> >       audit_log_format(ab, "sec=%lli nsec=%li",
> >         (long long)tk->tv_sec, tk->tv_nsec);
> >     }
> >     audit_log_end(ab);
>
> There is an audit_log_end() in the calling function, show_special(), so
> it should only be called here if there is another buffer allocated in
> this function after it.  audit_log_end() is protected should it be
> called with no valid buffer so this wouldn't create a bug.

As audit_log_end() can safely take a NULL audit_buffer I don't care if we send it back a valid buffer or a NULL.  IMO it happens to be easier (and cleaner) to send back a NULL.

> >     if (*abp) {
> >       *abp = NULL;
> >       type = (type == AUDIT_TIME_ADJNTPVAL ?
> >         AUDIT_TIME_INJOFFSET : AUDIT_TIME_ADJNTPVAL);
>
> This cannot be allocated if there are no more needed above ...

My mistake, I was distracted a few times while typing up my reply and the code within; while I had that detail in mind when I started I lost it during one of the interruptions.  As penance, I wrote up some slightly more proper code and at least made sure it complied, although I've not tried booting it ...

diff --git a/kernel/audit.c b/kernel/audit.c
index e4bbe2c70c26..f21024d8a402 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1737,7 +1737,11 @@ static int __init audit_backlog_limit_set(char *str)
 }
 __setup("audit_backlog_limit=", audit_backlog_limit_set);
 
-static void audit_buffer_free(struct audit_buffer *ab)
+/**
+ * audit_buffer_free - free an audit buffer
+ * @ab: the audit buffer to free
+ */
+void audit_buffer_free(struct audit_buffer *ab)
 {
        if (!ab)
                return;
diff --git a/kernel/audit.h b/kernel/audit.h
index c4498090a5bd..ba4c12b0d876 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -201,6 +201,10 @@ struct audit_context {
                struct {
                        char                    *name;
                } module;
+               struct {
+                       struct audit_ntp_data   ntp_data;
+                       struct timespec64       tk_injoffset;
+               } time;
        };
        int fds[2];
        struct audit_proctitle proctitle;
@@ -208,6 +212,8 @@ struct audit_context {
 
 extern bool audit_ever_enabled;
 
+extern void audit_buffer_free(struct audit_buffer *ab);
+
 extern void audit_log_session_info(struct audit_buffer *ab);
 
 extern int auditd_test_task(struct task_struct *task);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index fce5d43a933f..81434441aa13 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1340,6 +1340,76 @@ static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
                         from_kuid(&init_user_ns, name->fcap.rootid));
 }
 
+/**
+ * audit_log_time - log ntp/time related information
+ * @context: the audit context
+ * @ab: an audit buffer
+ *
+ * This is a helper function for show_special() and should not be called from
+ * any other context.  This function also fully consumes @ab such that the
+ * caller should not assume it to be valid on return.
+ */
+static void audit_log_time(struct audit_context *context,
+                          struct audit_buffer *ab)
+{
+       int i;
+       int type = context->type;
+       const char *ntp_name[] = {
+               "offset",
+               "freq",
+               "status",
+               "tai",
+               "tick",
+               "adjust",
+       };
+
+       do {
+               if (type == AUDIT_TIME_ADJNTPVAL) {
+                       struct audit_ntp_val *ntp = context->time.ntp_data.vals;
+
+                       for (i = 0; i < AUDIT_NTP_NVALS; i++) {
+                               if (ntp[i].newval == ntp[i].oldval)
+                                       continue;
+
+                               if (!ab)
+                                       ab = audit_log_start(context,
+                                                            GFP_KERNEL, type);
+                               audit_log_format(ab,
+                                                "op=%s old=%lli new=%lli",
+                                                ntp_name[i],
+                                                ntp[i].oldval, ntp[i].newval);
+                               audit_log_end(ab);
+                               ab = NULL;
+                       }
+
+                       type = (type == context->type ?
+                               AUDIT_TIME_INJOFFSET : 0);
+               } else if (type == AUDIT_TIME_INJOFFSET) {
+                       struct timespec64 *tk = &context->time.tk_injoffset;
+
+                       if (tk->tv_sec || tk->tv_nsec) {
+                               if (!ab)
+                                       ab = audit_log_start(context,
+                                                            GFP_KERNEL, type);
+                               audit_log_format(ab, "sec=%lli nsec=%li",
+                                               (long long)tk->tv_sec,
+                                                tk->tv_nsec);
+                               audit_log_end(ab);
+                               ab = NULL;
+                       }
+
+                       type = (type == context->type ?
+                               AUDIT_TIME_ADJNTPVAL : 0);
+               } else
+                       WARN_ONCE(1,
+                                 "audit_log_time() called with invalid context (%d)\n",
+                                 context->type);
+       } while (type);
+
+       /* ab should always be NULL here, but cleanup _just_in_case_ ... */
+       audit_buffer_free(ab);
+}
+
 static void show_special(struct audit_context *context, int *call_panic)
 {
        struct audit_buffer *ab;
@@ -1454,6 +1524,11 @@ static void show_special(struct audit_context *context, int *call_panic)
                        audit_log_format(ab, "(null)");
 
                break;
+       case AUDIT_TIME_ADJNTPVAL:
+       case AUDIT_TIME_INJOFFSET:
+               audit_log_time(context, ab);
+               ab = NULL;
+               break;
        }
        audit_log_end(ab);
 }
@@ -2849,21 +2924,25 @@ void __audit_fanotify(unsigned int response)
 
 void __audit_tk_injoffset(struct timespec64 offset)
 {
-       audit_log(audit_context(), GFP_KERNEL, AUDIT_TIME_INJOFFSET,
-                 "sec=%lli nsec=%li",
-                 (long long)offset.tv_sec, offset.tv_nsec);
+       struct audit_context *context = audit_context();
+
+       if (!context->type)
+               context->type = AUDIT_TIME_INJOFFSET;
+       memcpy(&context->time.tk_injoffset, &offset, sizeof(offset));
 }
 
 static void audit_log_ntp_val(const struct audit_ntp_data *ad,
                              const char *op, enum audit_ntp_type type)
 {
-       const struct audit_ntp_val *val = &ad->vals[type];
-
-       if (val->newval == val->oldval)
-               return;
+       int i;
+       struct audit_context *context = audit_context();
 
-       audit_log(audit_context(), GFP_KERNEL, AUDIT_TIME_ADJNTPVAL,
-                 "op=%s old=%lli new=%lli", op, val->oldval, val->newval);
+       for (i = 0; i < AUDIT_NTP_NVALS; i++)
+               if (ad->vals[i].newval != ad->vals[i].oldval) {
+                       context->type = AUDIT_TIME_ADJNTPVAL;
+                       memcpy(&context->time.ntp_data, ad, sizeof(*ad));
+                       break;
+               }
 }
 
 void __audit_ntp_log(const struct audit_ntp_data *ad)

--
paul-moore.com