Ondrej Mosnacek
2018-06-19 13:58:59 UTC
This patch adds a new function that shall be used to log any
modification of the system's clock (via the adjtimex() syscall).
The function logs an audit record of type AUDIT_TIME_ADJUSTED with the
following fields:
* txmodes (the 'modes' field of struct timex; this is the only
mandatory field)
* txsec, txusec (the 'tv_sec' and 'tv_usec' fields of the 'time' field
of struct timex (respectively))
* txoffset (the 'offset' field of struct timex)
* txfreq (the 'freq' field of struct timex)
* txmaxerr (the 'maxerror' field of struct timex)
* txesterr (the 'esterror' field of struct timex)
* txstatus (the 'status' field of struct timex)
* txconst (the 'constant' field of struct timex)
* txtick (the 'tick' field of struct timex)
These fields allow to reconstruct what exactly was changed by the
adjtimex syscall and to what value(s). Note that the values reported are
taken directly from the structure as received from userspace. The
syscall handling code may do some clamping of the values internally
before actually changing the kernel variables. Also, the fact that this
record has been logged does not necessarily mean that some variable was
changed (it may have been set to the same value as the old value).
Quick overview of the 'struct timex' semantics:
The 'modes' field is a bitmask specifying which time variables (if
any) should be adjusted. The other fields (of those listed above)
contain the values of the respective variables that should be set. If
the corresponding bit is not set in the 'modes' field, then a field's
value is not significant (it may be some garbage value passed from
userspace).
Note that after processing the input values from userspace, the
handler writes (all) the current (new) internal values into the struct
and hands it back to userspace. These values are not logged.
Also note that 'txusec' may actually mean nanoseconds, not
microseconds, depending on whether ADJ_NSEC is set in 'modes'.
Possible improvements:
* move the conditional for logging/not logging into audit_adjtime()
- (see also next patch in series)
- may not be desirable due to reasons above
- we should probably either do both this and above or neither
* log also the old values + the actual values that get set
- this would be rather difficult to do, probably not worth it
Signed-off-by: Ondrej Mosnacek <***@redhat.com>
---
include/linux/audit.h | 11 +++++++++++
kernel/auditsc.c | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 69c78477590b..26e9db46293c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <uapi/linux/audit.h>
+#include <uapi/linux/timex.h>
#define AUDIT_INO_UNSET ((unsigned long)-1)
#define AUDIT_DEV_UNSET ((dev_t)-1)
@@ -353,6 +354,7 @@ extern void __audit_log_capset(const struct cred *new, const struct cred *old);
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_adjtime(const struct timex *txc);
static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
@@ -455,6 +457,12 @@ static inline void audit_fanotify(unsigned int response)
__audit_fanotify(response);
}
+static inline void audit_adjtime(const struct timex *txc)
+{
+ if (!audit_dummy_context())
+ __audit_adjtime(txc);
+}
+
extern int audit_n_rules;
extern int audit_signals;
#else /* CONFIG_AUDITSYSCALL */
@@ -581,6 +589,9 @@ static inline void audit_log_kern_module(char *name)
static inline void audit_fanotify(unsigned int response)
{ }
+static inline void audit_adjtime(const struct timex *txc)
+{ }
+
static inline void audit_ptrace(struct task_struct *t)
{ }
#define audit_n_rules 0
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index ceb1c4596c51..9a1f3fcc377a 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2422,6 +2422,44 @@ void __audit_fanotify(unsigned int response)
AUDIT_FANOTIFY, "resp=%u", response);
}
+void __audit_adjtime(const struct timex *txc)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TIME_ADJUSTED);
+ if (unlikely(!ab))
+ return;
+
+ audit_log_format(ab, "txmodes=%u", txc->modes);
+
+ if (txc->modes & ADJ_SETOFFSET)
+ audit_log_format(ab, " txsec=%li txusec=%li",
+ (long)txc->time.tv_sec, (long)txc->time.tv_usec);
+
+ if (txc->modes & ADJ_OFFSET)
+ audit_log_format(ab, " txoffset=%li", txc->offset);
+
+ if (txc->modes & ADJ_FREQUENCY)
+ audit_log_format(ab, " txfreq=%li", txc->freq);
+
+ if (txc->modes & ADJ_MAXERROR)
+ audit_log_format(ab, " txmaxerr=%li", txc->maxerror);
+
+ if (txc->modes & ADJ_ESTERROR)
+ audit_log_format(ab, " txesterr=%li", txc->esterror);
+
+ if (txc->modes & ADJ_STATUS)
+ audit_log_format(ab, " txstatus=%li", txc->status);
+
+ if (txc->modes & (ADJ_TIMECONST | ADJ_TAI))
+ audit_log_format(ab, " txconst=%li", txc->constant);
+
+ if (txc->modes & ADJ_TICK)
+ audit_log_format(ab, " txtick=%li", txc->tick);
+
+ audit_log_end(ab);
+}
+
static void audit_log_task(struct audit_buffer *ab)
{
kuid_t auid, uid;
modification of the system's clock (via the adjtimex() syscall).
The function logs an audit record of type AUDIT_TIME_ADJUSTED with the
following fields:
* txmodes (the 'modes' field of struct timex; this is the only
mandatory field)
* txsec, txusec (the 'tv_sec' and 'tv_usec' fields of the 'time' field
of struct timex (respectively))
* txoffset (the 'offset' field of struct timex)
* txfreq (the 'freq' field of struct timex)
* txmaxerr (the 'maxerror' field of struct timex)
* txesterr (the 'esterror' field of struct timex)
* txstatus (the 'status' field of struct timex)
* txconst (the 'constant' field of struct timex)
* txtick (the 'tick' field of struct timex)
These fields allow to reconstruct what exactly was changed by the
adjtimex syscall and to what value(s). Note that the values reported are
taken directly from the structure as received from userspace. The
syscall handling code may do some clamping of the values internally
before actually changing the kernel variables. Also, the fact that this
record has been logged does not necessarily mean that some variable was
changed (it may have been set to the same value as the old value).
Quick overview of the 'struct timex' semantics:
The 'modes' field is a bitmask specifying which time variables (if
any) should be adjusted. The other fields (of those listed above)
contain the values of the respective variables that should be set. If
the corresponding bit is not set in the 'modes' field, then a field's
value is not significant (it may be some garbage value passed from
userspace).
Note that after processing the input values from userspace, the
handler writes (all) the current (new) internal values into the struct
and hands it back to userspace. These values are not logged.
Also note that 'txusec' may actually mean nanoseconds, not
microseconds, depending on whether ADJ_NSEC is set in 'modes'.
Possible improvements:
* move the conditional for logging/not logging into audit_adjtime()
- (see also next patch in series)
- may not be desirable due to reasons above
- we should probably either do both this and above or neither
* log also the old values + the actual values that get set
- this would be rather difficult to do, probably not worth it
Signed-off-by: Ondrej Mosnacek <***@redhat.com>
---
include/linux/audit.h | 11 +++++++++++
kernel/auditsc.c | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 69c78477590b..26e9db46293c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <uapi/linux/audit.h>
+#include <uapi/linux/timex.h>
#define AUDIT_INO_UNSET ((unsigned long)-1)
#define AUDIT_DEV_UNSET ((dev_t)-1)
@@ -353,6 +354,7 @@ extern void __audit_log_capset(const struct cred *new, const struct cred *old);
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_adjtime(const struct timex *txc);
static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
@@ -455,6 +457,12 @@ static inline void audit_fanotify(unsigned int response)
__audit_fanotify(response);
}
+static inline void audit_adjtime(const struct timex *txc)
+{
+ if (!audit_dummy_context())
+ __audit_adjtime(txc);
+}
+
extern int audit_n_rules;
extern int audit_signals;
#else /* CONFIG_AUDITSYSCALL */
@@ -581,6 +589,9 @@ static inline void audit_log_kern_module(char *name)
static inline void audit_fanotify(unsigned int response)
{ }
+static inline void audit_adjtime(const struct timex *txc)
+{ }
+
static inline void audit_ptrace(struct task_struct *t)
{ }
#define audit_n_rules 0
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index ceb1c4596c51..9a1f3fcc377a 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2422,6 +2422,44 @@ void __audit_fanotify(unsigned int response)
AUDIT_FANOTIFY, "resp=%u", response);
}
+void __audit_adjtime(const struct timex *txc)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TIME_ADJUSTED);
+ if (unlikely(!ab))
+ return;
+
+ audit_log_format(ab, "txmodes=%u", txc->modes);
+
+ if (txc->modes & ADJ_SETOFFSET)
+ audit_log_format(ab, " txsec=%li txusec=%li",
+ (long)txc->time.tv_sec, (long)txc->time.tv_usec);
+
+ if (txc->modes & ADJ_OFFSET)
+ audit_log_format(ab, " txoffset=%li", txc->offset);
+
+ if (txc->modes & ADJ_FREQUENCY)
+ audit_log_format(ab, " txfreq=%li", txc->freq);
+
+ if (txc->modes & ADJ_MAXERROR)
+ audit_log_format(ab, " txmaxerr=%li", txc->maxerror);
+
+ if (txc->modes & ADJ_ESTERROR)
+ audit_log_format(ab, " txesterr=%li", txc->esterror);
+
+ if (txc->modes & ADJ_STATUS)
+ audit_log_format(ab, " txstatus=%li", txc->status);
+
+ if (txc->modes & (ADJ_TIMECONST | ADJ_TAI))
+ audit_log_format(ab, " txconst=%li", txc->constant);
+
+ if (txc->modes & ADJ_TICK)
+ audit_log_format(ab, " txtick=%li", txc->tick);
+
+ audit_log_end(ab);
+}
+
static void audit_log_task(struct audit_buffer *ab)
{
kuid_t auid, uid;
--
2.17.1
2.17.1