|
@@ -139,6 +139,15 @@ static struct task_struct *kauditd_task;
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
|
|
|
|
|
|
+static struct audit_features af = {.vers = AUDIT_FEATURE_VERSION,
|
|
|
+ .mask = -1,
|
|
|
+ .features = 0,
|
|
|
+ .lock = 0,};
|
|
|
+
|
|
|
+static char *audit_feature_names[0] = {
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
/* Serialize requests from userspace. */
|
|
|
DEFINE_MUTEX(audit_cmd_mutex);
|
|
|
|
|
@@ -583,6 +592,8 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
|
|
|
return -EOPNOTSUPP;
|
|
|
case AUDIT_GET:
|
|
|
case AUDIT_SET:
|
|
|
+ case AUDIT_GET_FEATURE:
|
|
|
+ case AUDIT_SET_FEATURE:
|
|
|
case AUDIT_LIST_RULES:
|
|
|
case AUDIT_ADD_RULE:
|
|
|
case AUDIT_DEL_RULE:
|
|
@@ -627,6 +638,94 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type)
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+int is_audit_feature_set(int i)
|
|
|
+{
|
|
|
+ return af.features & AUDIT_FEATURE_TO_MASK(i);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int audit_get_feature(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ u32 seq;
|
|
|
+
|
|
|
+ seq = nlmsg_hdr(skb)->nlmsg_seq;
|
|
|
+
|
|
|
+ audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_GET, 0, 0,
|
|
|
+ &af, sizeof(af));
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature,
|
|
|
+ u32 old_lock, u32 new_lock, int res)
|
|
|
+{
|
|
|
+ struct audit_buffer *ab;
|
|
|
+
|
|
|
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
|
|
|
+ audit_log_format(ab, "feature=%s new=%d old=%d old_lock=%d new_lock=%d res=%d",
|
|
|
+ audit_feature_names[which], !!old_feature, !!new_feature,
|
|
|
+ !!old_lock, !!new_lock, res);
|
|
|
+ audit_log_end(ab);
|
|
|
+}
|
|
|
+
|
|
|
+static int audit_set_feature(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct audit_features *uaf;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > sizeof(audit_feature_names)/sizeof(audit_feature_names[0]));
|
|
|
+ uaf = nlmsg_data(nlmsg_hdr(skb));
|
|
|
+
|
|
|
+ /* if there is ever a version 2 we should handle that here */
|
|
|
+
|
|
|
+ for (i = 0; i <= AUDIT_LAST_FEATURE; i++) {
|
|
|
+ u32 feature = AUDIT_FEATURE_TO_MASK(i);
|
|
|
+ u32 old_feature, new_feature, old_lock, new_lock;
|
|
|
+
|
|
|
+ /* if we are not changing this feature, move along */
|
|
|
+ if (!(feature & uaf->mask))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ old_feature = af.features & feature;
|
|
|
+ new_feature = uaf->features & feature;
|
|
|
+ new_lock = (uaf->lock | af.lock) & feature;
|
|
|
+ old_lock = af.lock & feature;
|
|
|
+
|
|
|
+ /* are we changing a locked feature? */
|
|
|
+ if ((af.lock & feature) && (new_feature != old_feature)) {
|
|
|
+ audit_log_feature_change(i, old_feature, new_feature,
|
|
|
+ old_lock, new_lock, 0);
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* nothing invalid, do the changes */
|
|
|
+ for (i = 0; i <= AUDIT_LAST_FEATURE; i++) {
|
|
|
+ u32 feature = AUDIT_FEATURE_TO_MASK(i);
|
|
|
+ u32 old_feature, new_feature, old_lock, new_lock;
|
|
|
+
|
|
|
+ /* if we are not changing this feature, move along */
|
|
|
+ if (!(feature & uaf->mask))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ old_feature = af.features & feature;
|
|
|
+ new_feature = uaf->features & feature;
|
|
|
+ old_lock = af.lock & feature;
|
|
|
+ new_lock = (uaf->lock | af.lock) & feature;
|
|
|
+
|
|
|
+ if (new_feature != old_feature)
|
|
|
+ audit_log_feature_change(i, old_feature, new_feature,
|
|
|
+ old_lock, new_lock, 1);
|
|
|
+
|
|
|
+ if (new_feature)
|
|
|
+ af.features |= feature;
|
|
|
+ else
|
|
|
+ af.features &= ~feature;
|
|
|
+ af.lock |= new_lock;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
|
{
|
|
|
u32 seq;
|
|
@@ -698,6 +797,16 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
|
if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
|
|
|
err = audit_set_backlog_limit(status_get->backlog_limit);
|
|
|
break;
|
|
|
+ case AUDIT_GET_FEATURE:
|
|
|
+ err = audit_get_feature(skb);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
+ case AUDIT_SET_FEATURE:
|
|
|
+ err = audit_set_feature(skb);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
case AUDIT_USER:
|
|
|
case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
|
|
|
case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
|