Skip to content

Commit

Permalink
ntp: Audit NTP parameters adjustment
Browse files Browse the repository at this point in the history
Emit an audit record every time selected NTP parameters are modified
from userspace (via adjtimex(2) or clock_adjtime(2)). These parameters
may be used to indirectly change system clock, and thus their
modifications should be audited.

Such events will now generate records of type AUDIT_TIME_ADJNTPVAL
containing the following fields:
  - op -- which value was adjusted:
    - offset -- corresponding to the time_offset variable
    - freq   -- corresponding to the time_freq variable
    - status -- corresponding to the time_status variable
    - adjust -- corresponding to the time_adjust variable
    - tick   -- corresponding to the tick_usec variable
    - tai    -- corresponding to the timekeeping's TAI offset
  - old -- the old value
  - new -- the new value

Example records:

type=TIME_ADJNTPVAL msg=audit(1530616044.507:7): op=status old=64 new=8256
type=TIME_ADJNTPVAL msg=audit(1530616044.511:11): op=freq old=0 new=49180377088000

The records of this type will be associated with the corresponding
syscall records.

An overview of parameter changes that can be done via do_adjtimex()
(based on information from Miroslav Lichvar) and whether they are
audited:
  __timekeeping_set_tai_offset() -- sets the offset from the
                                    International Atomic Time
                                    (AUDITED)
  NTP variables:
    time_offset -- can adjust the clock by up to 0.5 seconds per call
                   and also speed it up or slow down by up to about
                   0.05% (43 seconds per day) (AUDITED)
    time_freq -- can speed up or slow down by up to about 0.05%
                 (AUDITED)
    time_status -- can insert/delete leap seconds and it also enables/
                   disables synchronization of the hardware real-time
                   clock (AUDITED)
    time_maxerror, time_esterror -- change error estimates used to
                                    inform userspace applications
                                    (NOT AUDITED)
    time_constant -- controls the speed of the clock adjustments that
                     are made when time_offset is set (NOT AUDITED)
    time_adjust -- can temporarily speed up or slow down the clock by up
                   to 0.05% (AUDITED)
    tick_usec -- a more extreme version of time_freq; can speed up or
                 slow down the clock by up to 10% (AUDITED)

Signed-off-by: Ondrej Mosnacek <[email protected]>
Reviewed-by: Richard Guy Briggs <[email protected]>
Reviewed-by: Thomas Gleixner <[email protected]>
Signed-off-by: Paul Moore <[email protected]>
  • Loading branch information
WOnder93 authored and pcmoore committed Apr 15, 2019
1 parent 2d87a06 commit 7e8eda7
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 5 deletions.
61 changes: 61 additions & 0 deletions include/linux/audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,29 @@ struct audit_field {
u32 op;
};

enum audit_ntp_type {
AUDIT_NTP_OFFSET,
AUDIT_NTP_FREQ,
AUDIT_NTP_STATUS,
AUDIT_NTP_TAI,
AUDIT_NTP_TICK,
AUDIT_NTP_ADJUST,

AUDIT_NTP_NVALS /* count */
};

#ifdef CONFIG_AUDITSYSCALL
struct audit_ntp_val {
long long oldval, newval;
};

struct audit_ntp_data {
struct audit_ntp_val vals[AUDIT_NTP_NVALS];
};
#else
struct audit_ntp_data {};
#endif

extern int is_audit_feature_set(int which);

extern int __init audit_register_class(int class, unsigned *list);
Expand Down Expand Up @@ -366,6 +389,7 @@ extern void __audit_mmap_fd(int fd, int flags);
extern void __audit_log_kern_module(char *name);
extern void __audit_fanotify(unsigned int response);
extern void __audit_tk_injoffset(struct timespec64 offset);
extern void __audit_ntp_log(const struct audit_ntp_data *ad);

static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
Expand Down Expand Up @@ -478,6 +502,29 @@ static inline void audit_tk_injoffset(struct timespec64 offset)
__audit_tk_injoffset(offset);
}

static inline void audit_ntp_init(struct audit_ntp_data *ad)
{
memset(ad, 0, sizeof(*ad));
}

static inline void audit_ntp_set_old(struct audit_ntp_data *ad,
enum audit_ntp_type type, long long val)
{
ad->vals[type].oldval = val;
}

static inline void audit_ntp_set_new(struct audit_ntp_data *ad,
enum audit_ntp_type type, long long val)
{
ad->vals[type].newval = val;
}

static inline void audit_ntp_log(const struct audit_ntp_data *ad)
{
if (!audit_dummy_context())
__audit_ntp_log(ad);
}

extern int audit_n_rules;
extern int audit_signals;
#else /* CONFIG_AUDITSYSCALL */
Expand Down Expand Up @@ -594,6 +641,20 @@ static inline void audit_fanotify(unsigned int response)
static inline void audit_tk_injoffset(struct timespec64 offset)
{ }

static inline void audit_ntp_init(struct audit_ntp_data *ad)
{ }

static inline void audit_ntp_set_old(struct audit_ntp_data *ad,
enum audit_ntp_type type, long long val)
{ }

static inline void audit_ntp_set_new(struct audit_ntp_data *ad,
enum audit_ntp_type type, long long val)
{ }

static inline void audit_ntp_log(const struct audit_ntp_data *ad)
{ }

static inline void audit_ptrace(struct task_struct *t)
{ }
#define audit_n_rules 0
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
#define AUDIT_KERN_MODULE 1330 /* Kernel Module events */
#define AUDIT_FANOTIFY 1331 /* Fanotify access decision */
#define AUDIT_TIME_INJOFFSET 1332 /* Timekeeping offset injected */
#define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */

#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
Expand Down
22 changes: 22 additions & 0 deletions kernel/auditsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2519,6 +2519,28 @@ void __audit_tk_injoffset(struct timespec64 offset)
(long long)offset.tv_sec, offset.tv_nsec);
}

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;

audit_log(audit_context(), GFP_KERNEL, AUDIT_TIME_ADJNTPVAL,
"op=%s old=%lli new=%lli", op, val->oldval, val->newval);
}

void __audit_ntp_log(const struct audit_ntp_data *ad)
{
audit_log_ntp_val(ad, "offset", AUDIT_NTP_OFFSET);
audit_log_ntp_val(ad, "freq", AUDIT_NTP_FREQ);
audit_log_ntp_val(ad, "status", AUDIT_NTP_STATUS);
audit_log_ntp_val(ad, "tai", AUDIT_NTP_TAI);
audit_log_ntp_val(ad, "tick", AUDIT_NTP_TICK);
audit_log_ntp_val(ad, "adjust", AUDIT_NTP_ADJUST);
}

static void audit_log_task(struct audit_buffer *ab)
{
kuid_t auid, uid;
Expand Down
22 changes: 19 additions & 3 deletions kernel/time/ntp.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/audit.h>

#include "ntp_internal.h"
#include "timekeeping_internal.h"
Expand Down Expand Up @@ -709,7 +710,7 @@ static inline void process_adjtimex_modes(const struct __kernel_timex *txc,
* kernel time-keeping variables. used by xntpd.
*/
int __do_adjtimex(struct __kernel_timex *txc, const struct timespec64 *ts,
s32 *time_tai)
s32 *time_tai, struct audit_ntp_data *ad)
{
int result;

Expand All @@ -720,14 +721,29 @@ int __do_adjtimex(struct __kernel_timex *txc, const struct timespec64 *ts,
/* adjtime() is independent from ntp_adjtime() */
time_adjust = txc->offset;
ntp_update_frequency();

audit_ntp_set_old(ad, AUDIT_NTP_ADJUST, save_adjust);
audit_ntp_set_new(ad, AUDIT_NTP_ADJUST, time_adjust);
}
txc->offset = save_adjust;
} else {

/* If there are input parameters, then process them: */
if (txc->modes)
if (txc->modes) {
audit_ntp_set_old(ad, AUDIT_NTP_OFFSET, time_offset);
audit_ntp_set_old(ad, AUDIT_NTP_FREQ, time_freq);
audit_ntp_set_old(ad, AUDIT_NTP_STATUS, time_status);
audit_ntp_set_old(ad, AUDIT_NTP_TAI, *time_tai);
audit_ntp_set_old(ad, AUDIT_NTP_TICK, tick_usec);

process_adjtimex_modes(txc, time_tai);

audit_ntp_set_new(ad, AUDIT_NTP_OFFSET, time_offset);
audit_ntp_set_new(ad, AUDIT_NTP_FREQ, time_freq);
audit_ntp_set_new(ad, AUDIT_NTP_STATUS, time_status);
audit_ntp_set_new(ad, AUDIT_NTP_TAI, *time_tai);
audit_ntp_set_new(ad, AUDIT_NTP_TICK, tick_usec);
}

txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ,
NTP_SCALE_SHIFT);
if (!(time_status & STA_NANO))
Expand Down
4 changes: 3 additions & 1 deletion kernel/time/ntp_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ extern void ntp_clear(void);
extern u64 ntp_tick_length(void);
extern ktime_t ntp_get_next_leap(void);
extern int second_overflow(time64_t secs);
extern int __do_adjtimex(struct __kernel_timex *txc, const struct timespec64 *ts, s32 *time_tai);
extern int __do_adjtimex(struct __kernel_timex *txc,
const struct timespec64 *ts,
s32 *time_tai, struct audit_ntp_data *ad);
extern void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts);
#endif /* _LINUX_NTP_INTERNAL_H */
7 changes: 6 additions & 1 deletion kernel/time/timekeeping.c
Original file line number Diff line number Diff line change
Expand Up @@ -2307,6 +2307,7 @@ static int timekeeping_validate_timex(const struct __kernel_timex *txc)
int do_adjtimex(struct __kernel_timex *txc)
{
struct timekeeper *tk = &tk_core.timekeeper;
struct audit_ntp_data ad;
unsigned long flags;
struct timespec64 ts;
s32 orig_tai, tai;
Expand All @@ -2330,13 +2331,15 @@ int do_adjtimex(struct __kernel_timex *txc)
audit_tk_injoffset(delta);
}

audit_ntp_init(&ad);

ktime_get_real_ts64(&ts);

raw_spin_lock_irqsave(&timekeeper_lock, flags);
write_seqcount_begin(&tk_core.seq);

orig_tai = tai = tk->tai_offset;
ret = __do_adjtimex(txc, &ts, &tai);
ret = __do_adjtimex(txc, &ts, &tai, &ad);

if (tai != orig_tai) {
__timekeeping_set_tai_offset(tk, tai);
Expand All @@ -2347,6 +2350,8 @@ int do_adjtimex(struct __kernel_timex *txc)
write_seqcount_end(&tk_core.seq);
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);

audit_ntp_log(&ad);

/* Update the multiplier immediately if frequency was set directly */
if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
timekeeping_advance(TK_ADV_FREQ);
Expand Down

0 comments on commit 7e8eda7

Please sign in to comment.