On 2022-01-31 20:29, Paul Moore wrote: > On Mon, Jan 31, 2022 at 6:29 PM Richard Guy Briggs <r...@redhat.com> wrote: > > On 2022-01-31 17:02, Paul Moore wrote: > > > On Wed, Jan 26, 2022 at 8:52 AM Richard Guy Briggs <r...@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 <r...@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.
None of the other callees in show_special() do that, so this would be surprising behaviour that could cause a future bug. This was part of the UMN CS&E strategy. > > > 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 ... Did you test the code I submitted? It compiles and works. I found this code harder to follow. This was partly why I wanted to leave one of the record types outside of show_special() but I did find a way to accomodate both with a minimum of overhead. Speaking of distracted... I'm within earshot of a trucker convoy that has dug in with horns blaring (with at least 4 train horns) attempting to overthrow our government so I'm a bit rattled and I've had a bit of trouble focussing for the last two weeks... > 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); Why the conditional? If it made it this far, the next type, if it passes the condition below will be TK. > + } 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); Why? This is unnecessary since if the type is TK, the type can't be NTP. > + } else > + WARN_ONCE(1, > + "audit_log_time() called with invalid > context (%d)\n", > + context->type); This code is unnecessary since it can never be executed. > + } while (type); > + > + /* ab should always be NULL here, but cleanup _just_in_case_ ... */ > + audit_buffer_free(ab); We never need a buffer free since it is taken care of above if the logic is correct and isn't needed if we let the calling function call audit_log_end(). > +} > + > 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; > + } > } This code might compile, but it won't work since audit_log_ntp_val() is never called and the values are never set. > void __audit_ntp_log(const struct audit_ntp_data *ad) > > -- > paul-moore.com - RGB -- Richard Guy Briggs <r...@redhat.com> Sr. S/W Engineer, Kernel Security, Base Operating Systems Remote, Ottawa, Red Hat Canada IRC: rgb, SunRaycer Voice: +1.647.777.2635, Internal: (81) 32635 -- Linux-audit mailing list Linux-audit@redhat.com https://listman.redhat.com/mailman/listinfo/linux-audit