[PATCH] audit keys: support for multiple audit keys

Juraj Hlista juro.hlista at gmail.com
Fri Mar 12 07:44:22 UTC 2010


An audit rule can have more than 1 key, the keys can be of
different types (only AUDIT_FILTERKEY for now)

For example, it is possible to create a rule such as:
   auditctl -a exit,always -F path=/file -F key=k1 -F key=k2 -F key=k3


Kernel patch:
---
 include/linux/audit.h |   11 ++-
 kernel/audit.c        |   12 ++--
 kernel/audit_tree.c   |    4 +-
 kernel/audit_watch.c  |    5 +-
 kernel/auditfilter.c  |  167 +++++++++++++++++++++++++++++++++++--------------
 kernel/auditsc.c      |   81 ++++++++++++++++++------
 6 files changed, 204 insertions(+), 76 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index f391d45..bc77a9f 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -152,10 +152,12 @@
 #define AUDIT_POSSIBLE 1       /* Build context if rule matches  */
 #define AUDIT_ALWAYS   2       /* Generate audit record if rule matches */

+
 /* Rule structure sizes -- if these change, different AUDIT_ADD and
 * AUDIT_LIST commands must be implemented. */
 #define AUDIT_MAX_FIELDS   64
 #define AUDIT_MAX_KEY_LEN  256
+#define AUDIT_MAX_KEYS    8
 #define AUDIT_BITMASK_SIZE 64
 #define AUDIT_WORD(nr) ((__u32)((nr)/32))
 #define AUDIT_BIT(nr)  (1 << ((nr) - AUDIT_WORD(nr)*32))
@@ -384,8 +386,9 @@ struct audit_krule {
       u32                     action;
       u32                     mask[AUDIT_BITMASK_SIZE];
       u32                     buflen; /* for data alloc on list rules */
+       u32                     keyfield_count;
       u32                     field_count;
-       char                    *filterkey; /* ties events to rules */
+       struct audit_field      *keyfields;
       struct audit_field      *fields;
       struct audit_field      *arch_f; /* quick access to arch field */
       struct audit_field      *inode_f; /* quick access to an inode field */
@@ -598,8 +601,8 @@ extern void             audit_log_untrustedstring(struct
audit_buffer *ab,
 extern void                audit_log_d_path(struct audit_buffer *ab,
                                            const char *prefix,
                                            struct path *path);
-extern void                audit_log_key(struct audit_buffer *ab,
-                                         char *key);
+extern void                audit_log_key(struct audit_buffer *ab, int type,
+                                               char *key);
 extern void                audit_log_lost(const char *message);
 extern int                 audit_update_lsm_rules(void);

@@ -622,7 +625,7 @@ extern int audit_enabled;
 #define audit_log_n_untrustedstring(a,n,s) do { ; } while (0)
 #define audit_log_untrustedstring(a,s) do { ; } while (0)
 #define audit_log_d_path(b, p, d) do { ; } while (0)
-#define audit_log_key(b, k) do { ; } while (0)
+#define audit_log_key(b, t, k) do { ; } while (0)
 #define audit_enabled 0
 #endif
 #endif
diff --git a/kernel/audit.c b/kernel/audit.c
index c3b6cb5..18090c0 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1440,13 +1440,15 @@ void audit_log_d_path(struct audit_buffer *ab,
const char *prefix,
       kfree(pathname);
 }

-void audit_log_key(struct audit_buffer *ab, char *key)
+void audit_log_key(struct audit_buffer *ab, int type, char *key)
 {
-       audit_log_format(ab, " key=");
-       if (key)
+       if (!key) {
+               audit_log_format(ab, " key=(null)");
+               return;
+       } else if (type == AUDIT_FILTERKEY) {
+               audit_log_format(ab, " key=");
               audit_log_untrustedstring(ab, key);
-       else
-               audit_log_format(ab, "(null)");
+       }
 }

 /**
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index f671d6b..f45c4d0 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -454,6 +454,7 @@ static void kill_rules(struct audit_tree *tree)
       struct audit_krule *rule, *next;
       struct audit_entry *entry;
       struct audit_buffer *ab;
+       int i;

       list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
               entry = container_of(rule, struct audit_entry, rule);
@@ -466,7 +467,8 @@ static void kill_rules(struct audit_tree *tree)
                       audit_log_string(ab, "remove rule");
                       audit_log_format(ab, " dir=");
                       audit_log_untrustedstring(ab, rule->tree->pathname);
-                       audit_log_key(ab, rule->filterkey);
+                       for (i = 0; i < rule->keyfield_count; i++)
+                               audit_log_key(ab,
rule->keyfields[i].type, rule->keyfields[i].lsm_str);
                       audit_log_format(ab, " list=%d res=1", rule->listnr);
                       audit_log_end(ab);
                       rule->tree = NULL;
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 31f9be8..5a306e5 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -240,6 +240,8 @@ static void audit_watch_log_rule_change(struct
audit_krule *r, struct audit_watc
 {
       if (audit_enabled) {
               struct audit_buffer *ab;
+               int i;
+
               ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
               audit_log_format(ab, "auid=%u ses=%u op=",
                                audit_get_loginuid(current),
@@ -247,7 +249,8 @@ static void audit_watch_log_rule_change(struct
audit_krule *r, struct audit_watc
               audit_log_string(ab, op);
               audit_log_format(ab, " path=");
               audit_log_untrustedstring(ab, w->path);
-               audit_log_key(ab, r->filterkey);
+               for (i = 0; i < r->keyfield_count; i++)
+                       audit_log_key(ab, r->keyfields[i].type,
r->keyfields[i].lsm_str);
               audit_log_format(ab, " list=%d res=1", r->listnr);
               audit_log_end(ab);
       }
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index f5e4cae..79c4978 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -74,14 +74,24 @@ static inline void audit_free_rule(struct audit_entry *e)
       /* some rules don't have associated watches */
       if (erule->watch)
               audit_put_watch(erule->watch);
-       if (erule->fields)
+       if (erule->fields) {
               for (i = 0; i < erule->field_count; i++) {
                       struct audit_field *f = &erule->fields[i];
                       kfree(f->lsm_str);
                       security_audit_rule_free(f->lsm_rule);
               }
+       }
       kfree(erule->fields);
-       kfree(erule->filterkey);
+
+       if (erule->keyfields) {
+               for (i = 0; i < erule->keyfield_count; i++) {
+                       struct audit_field *f = &erule->keyfields[i];
+                       kfree(f->lsm_str);
+                       security_audit_rule_free(f->lsm_rule);
+               }
+       }
+       kfree(erule->keyfields);
+
       kfree(e);
 }

@@ -92,10 +102,11 @@ void audit_free_rule_rcu(struct rcu_head *head)
 }

 /* Initialize an audit filterlist entry. */
-static inline struct audit_entry *audit_init_entry(u32 field_count)
+static inline struct audit_entry *audit_init_entry(u32 field_count,
u32 keyfield_count)
 {
       struct audit_entry *entry;
       struct audit_field *fields;
+       struct audit_field *keyfields;

       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
       if (unlikely(!entry))
@@ -108,6 +119,14 @@ static inline struct audit_entry
*audit_init_entry(u32 field_count)
       }
       entry->rule.fields = fields;

+       keyfields = kzalloc(sizeof(*keyfields) * keyfield_count, GFP_KERNEL);
+       if (unlikely(!keyfields)) {
+               kfree(entry);
+               kfree(fields);
+               return NULL;
+       }
+       entry->rule.keyfields = keyfields;
+
       return entry;
 }

@@ -151,6 +170,15 @@ static inline int audit_to_inode(struct audit_krule *krule,
       return 0;
 }

+static inline int audit_to_key(struct audit_krule *krule,
+                              struct audit_field *f)
+{
+       if (krule->listnr != AUDIT_FILTER_EXIT ||
+           f->op != Audit_equal)
+               return -EINVAL;
+
+       return 0;
+}
 static __u32 *classes[AUDIT_SYSCALL_CLASSES];

 int __init audit_register_class(int class, unsigned *list)
@@ -227,6 +255,7 @@ static inline struct audit_entry
*audit_to_entry_common(struct audit_rule *rule)
 {
       unsigned listnr;
       struct audit_entry *entry;
+       int kf_count = 0;
       int i, err;

       err = -EINVAL;
@@ -252,15 +281,24 @@ static inline struct audit_entry
*audit_to_entry_common(struct audit_rule *rule)
       if (rule->field_count > AUDIT_MAX_FIELDS)
               goto exit_err;

+       for (i = 0; i < rule->field_count; i++) {
+               if (rule->fields[i] == AUDIT_FILTERKEY)
+                       kf_count++;
+       }
+
+       if (kf_count > AUDIT_MAX_KEYS)
+               goto exit_err;
+
       err = -ENOMEM;
-       entry = audit_init_entry(rule->field_count);
+       entry = audit_init_entry(rule->field_count - kf_count, kf_count);
       if (!entry)
               goto exit_err;

       entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
       entry->rule.listnr = listnr;
       entry->rule.action = rule->action;
-       entry->rule.field_count = rule->field_count;
+       entry->rule.field_count = rule->field_count - kf_count;
+       entry->rule.keyfield_count = kf_count;

       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
               entry->rule.mask[i] = rule->mask[i];
@@ -411,10 +449,11 @@ static struct audit_entry
*audit_data_to_entry(struct audit_rule_data *data,
                                              size_t datasz)
 {
       int err = 0;
-       struct audit_entry *entry;
+       struct audit_entry *entry = NULL;
       void *bufp;
       size_t remain = datasz - sizeof(struct audit_rule_data);
       int i;
+       int f_count = 0, kf_count = 0;
       char *str;

       entry = audit_to_entry_common((struct audit_rule *)data);
@@ -424,7 +463,11 @@ static struct audit_entry
*audit_data_to_entry(struct audit_rule_data *data,
       bufp = data->buf;
       entry->rule.vers_ops = 2;
       for (i = 0; i < data->field_count; i++) {
-               struct audit_field *f = &entry->rule.fields[i];
+               struct audit_field *f = NULL;
+               if (data->fields[i] == AUDIT_FILTERKEY)
+                       f = &entry->rule.keyfields[kf_count++];
+               else
+                       f = &entry->rule.fields[f_count++];

               err = -EINVAL;

@@ -522,13 +565,20 @@ static struct audit_entry
*audit_data_to_entry(struct audit_rule_data *data,
                       break;
               case AUDIT_FILTERKEY:
                       err = -EINVAL;
-                       if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN)
+                       if (f->val > AUDIT_MAX_KEY_LEN)
                               goto exit_free;
                       str = audit_unpack_string(&bufp, &remain, f->val);
                       if (IS_ERR(str))
                               goto exit_free;
                       entry->rule.buflen += f->val;
-                       entry->rule.filterkey = str;
+
+                       err = audit_to_key(&entry->rule, f);
+                       if (err) {
+                               kfree(str);
+                               goto exit_free;
+                       } else {
+                               f->lsm_str = str;
+                       }
                       break;
               case AUDIT_PERM:
                       if (f->val & ~15)
@@ -565,6 +615,18 @@ static inline size_t audit_pack_string(void
**bufp, const char *str)
       return len;
 }

+
+static inline void audit_op_translate(struct audit_krule *krule,
struct audit_rule *rule, int j)
+{
+       if (krule->vers_ops == 1) {
+               if (krule->fields[j].op == Audit_not_equal)
+                               rule->fields[j] |= AUDIT_NEGATE;
+               } else {
+                       rule->fields[j] |= audit_ops[krule->fields[j].op];
+       }
+}
+
+
 /* Translate kernel rule respresentation to struct audit_rule.
 * Exists for backward compatibility with userspace. */
 static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
@@ -578,19 +640,20 @@ static struct audit_rule
*audit_krule_to_rule(struct audit_krule *krule)

       rule->flags = krule->flags | krule->listnr;
       rule->action = krule->action;
-       rule->field_count = krule->field_count;
-       for (i = 0; i < rule->field_count; i++) {
+       rule->field_count = krule->field_count + krule->keyfield_count;
+       for (i = 0; i < krule->field_count; i++) {
               rule->values[i] = krule->fields[i].val;
               rule->fields[i] = krule->fields[i].type;
-
-               if (krule->vers_ops == 1) {
-                       if (krule->fields[i].op == Audit_not_equal)
-                               rule->fields[i] |= AUDIT_NEGATE;
-               } else {
-                       rule->fields[i] |= audit_ops[krule->fields[i].op];
-               }
+               audit_op_translate(krule, rule, i);
       }
-       for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
+       for (i = 0; i < krule->keyfield_count; i++) {
+               rule->values[i] = krule->keyfields[i].val;
+               rule->fields[i] = krule->keyfields[i].type;
+               audit_op_translate(krule, rule, i);
+       }
+
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+               rule->mask[i] = krule->mask[i];

       return rule;
 }
@@ -600,7 +663,7 @@ static struct audit_rule_data
*audit_krule_to_data(struct audit_krule *krule)
 {
       struct audit_rule_data *data;
       void *bufp;
-       int i;
+       int i, j;

       data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
       if (unlikely(!data))
@@ -609,9 +672,9 @@ static struct audit_rule_data
*audit_krule_to_data(struct audit_krule *krule)

       data->flags = krule->flags | krule->listnr;
       data->action = krule->action;
-       data->field_count = krule->field_count;
+       data->field_count = krule->field_count + krule->keyfield_count;
       bufp = data->buf;
-       for (i = 0; i < data->field_count; i++) {
+       for (i = 0; i < krule->field_count; i++) {
               struct audit_field *f = &krule->fields[i];

               data->fields[i] = f->type;
@@ -640,15 +703,20 @@ static struct audit_rule_data
*audit_krule_to_data(struct audit_krule *krule)
                               audit_pack_string(&bufp,
                                                 audit_tree_path(krule->tree));
                       break;
-               case AUDIT_FILTERKEY:
-                       data->buflen += data->values[i] =
-                               audit_pack_string(&bufp, krule->filterkey);
-                       break;
               default:
                       data->values[i] = f->val;
               }
       }
-       for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i];
+       j = i;
+       for (i = 0; i < krule->keyfield_count; i++, j++) {
+               struct audit_field *f = &krule->keyfields[i];
+
+               data->fields[j] = f->type;
+               data->fieldflags[j] = audit_ops[f->op];
+               data->buflen += data->values[j] =
audit_pack_string(&bufp, f->lsm_str);
+       }
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+               data->mask[i] = krule->mask[i];

       return data;
 }
@@ -662,7 +730,8 @@ static int audit_compare_rule(struct audit_krule
*a, struct audit_krule *b)
       if (a->flags != b->flags ||
           a->listnr != b->listnr ||
           a->action != b->action ||
-           a->field_count != b->field_count)
+           a->field_count != b->field_count ||
+           a->keyfield_count != b->keyfield_count)
               return 1;

       for (i = 0; i < a->field_count; i++) {
@@ -694,17 +763,17 @@ static int audit_compare_rule(struct audit_krule
*a, struct audit_krule *b)
                                  audit_tree_path(b->tree)))
                               return 1;
                       break;
-               case AUDIT_FILTERKEY:
-                       /* both filterkeys exist based on above type compare */
-                       if (strcmp(a->filterkey, b->filterkey))
-                               return 1;
-                       break;
               default:
                       if (a->fields[i].val != b->fields[i].val)
                               return 1;
               }
       }
-
+       for (i = 0; i < a->keyfield_count; i++) {
+               if (a->keyfields[i].type != b->keyfields[i].type ||
+                   a->keyfields[i].op != b->keyfields[i].op ||
+                   strcmp(a->keyfields[i].lsm_str, b->keyfields[i].lsm_str))
+                       return 1;
+       }
       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
               if (a->mask[i] != b->mask[i])
                       return 1;
@@ -748,13 +817,13 @@ static inline int audit_dupe_lsm_field(struct
audit_field *df,
 * the initial copy. */
 struct audit_entry *audit_dupe_rule(struct audit_krule *old)
 {
-       u32 fcount = old->field_count;
+       u32 f_count = old->field_count;
+       u32 kf_count = old->keyfield_count;
       struct audit_entry *entry;
       struct audit_krule *new;
-       char *fk;
       int i, err = 0;

-       entry = audit_init_entry(fcount);
+       entry = audit_init_entry(f_count, kf_count);
       if (unlikely(!entry))
               return ERR_PTR(-ENOMEM);

@@ -768,6 +837,7 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old)
       new->prio = old->prio;
       new->buflen = old->buflen;
       new->inode_f = old->inode_f;
+       new->keyfield_count = old->keyfield_count;
       new->field_count = old->field_count;

       /*
@@ -778,11 +848,12 @@ struct audit_entry *audit_dupe_rule(struct
audit_krule *old)
        * the beginning of list scan.
        */
       new->tree = old->tree;
-       memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
+       memcpy(new->keyfields, old->keyfields, sizeof(struct
audit_field) * kf_count);
+       memcpy(new->fields, old->fields, sizeof(struct audit_field) * f_count);

       /* deep copy this information, updating the lsm_rule fields, because
        * the originals will all be freed when the old rule is freed. */
-       for (i = 0; i < fcount; i++) {
+       for (i = 0; i < f_count; i++) {
               switch (new->fields[i].type) {
               case AUDIT_SUBJ_USER:
               case AUDIT_SUBJ_ROLE:
@@ -797,19 +868,21 @@ struct audit_entry *audit_dupe_rule(struct
audit_krule *old)
                       err = audit_dupe_lsm_field(&new->fields[i],
                                                      &old->fields[i]);
                       break;
-               case AUDIT_FILTERKEY:
-                       fk = kstrdup(old->filterkey, GFP_KERNEL);
-                       if (unlikely(!fk))
-                               err = -ENOMEM;
-                       else
-                               new->filterkey = fk;
               }
               if (err) {
                       audit_free_rule(entry);
                       return ERR_PTR(err);
               }
       }
+       for (i = 0; i < kf_count; i++) {
+               err = audit_dupe_lsm_field(&new->keyfields[i],
+                                              &old->keyfields[i]);

+               if (err) {
+                       audit_free_rule(entry);
+                       return ERR_PTR(err);
+               }
+       }
       if (old->watch) {
               audit_get_watch(old->watch);
               new->watch = old->watch;
@@ -1053,6 +1126,7 @@ static void audit_log_rule_change(uid_t
loginuid, u32 sessionid, u32 sid,
                                 int res)
 {
       struct audit_buffer *ab;
+       int i;

       if (!audit_enabled)
               return;
@@ -1073,7 +1147,8 @@ static void audit_log_rule_change(uid_t
loginuid, u32 sessionid, u32 sid,
       }
       audit_log_format(ab, " op=");
       audit_log_string(ab, action);
-       audit_log_key(ab, rule->filterkey);
+       for (i = 0; i < rule->keyfield_count; i++)
+               audit_log_key(ab, rule->keyfields[i].type,
rule->keyfields[i].lsm_str);
       audit_log_format(ab, " list=%d res=%d", rule->listnr, res);
       audit_log_end(ab);
 }
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index f286982..1760dcd 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -161,6 +161,11 @@ struct audit_tree_refs {
       struct audit_chunk *c[31];
 };

+struct audit_keys {
+       int type;
+       char *key;
+};
+
 /* The per-task audit context. */
 struct audit_context {
       int                 dummy;      /* must be the first element */
@@ -175,7 +180,8 @@ struct audit_context {
       int                 return_valid; /* return code is valid */
       int                 name_count;
       struct audit_names  names[AUDIT_NAMES];
-       char *              filterkey;  /* key for rule that triggered record */
+       int                 key_count;
+       struct audit_keys   keys[AUDIT_MAX_KEYS];
       struct path         pwd;
       struct audit_context *previous; /* For nested syscalls */
       struct audit_aux_data *aux;
@@ -641,9 +647,14 @@ static int audit_filter_rules(struct task_struct *tsk,
       if (ctx) {
               if (rule->prio <= ctx->prio)
                       return 0;
-               if (rule->filterkey) {
-                       kfree(ctx->filterkey);
-                       ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
+               ctx->key_count = rule->keyfield_count;
+               for (i = 0; i < ctx->key_count; i++) {
+                       ctx->keys[i].type = rule->keyfields[i].type;
+                       if (rule->keyfields[i].lsm_str) {
+                               if (ctx->keys[i].key)
+                                       kfree(ctx->keys[i].key);
+                       }
+                       ctx->keys[i].key =
kstrdup(rule->keyfields[i].lsm_str, GFP_ATOMIC);
               }
               ctx->prio = rule->prio;
       }
@@ -659,16 +670,24 @@ static int audit_filter_rules(struct task_struct *tsk,
 * completely disabled for this task.  Since we only have the task
 * structure at this point, we can only check uid and gid.
 */
-static enum audit_state audit_filter_task(struct task_struct *tsk, char **key)
+static enum audit_state audit_filter_task(struct task_struct *tsk,
struct audit_keys *k, int *count)
 {
       struct audit_entry *e;
       enum audit_state   state;
+       int i, key_count;

       rcu_read_lock();
       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
               if (audit_filter_rules(tsk, &e->rule, NULL, NULL, &state)) {
-                       if (state == AUDIT_RECORD_CONTEXT)
-                               *key = kstrdup(e->rule.filterkey, GFP_ATOMIC);
+                       if (state == AUDIT_RECORD_CONTEXT) {
+                               key_count = e->rule.keyfield_count;
+
+                               for (i = 0; key_count; i++) {
+                                       k[i].type = e->rule.keyfields[i].type;
+                                       k[i].key =
kstrdup(e->rule.keyfields[i].lsm_str, GFP_ATOMIC);
+                               }
+                               *count = key_count;
+                       }
                       rcu_read_unlock();
                       return state;
               }
@@ -822,6 +841,16 @@ static inline void audit_free_names(struct
audit_context *context)
       context->pwd.mnt = NULL;
 }

+static inline void audit_free_keys(struct audit_context *context)
+{
+       int i;
+
+       for (i = 0; i < context->key_count; i++) {
+               kfree(context->keys[i].key);
+               context->keys[i].key = NULL;
+       }
+}
+
 static inline void audit_free_aux(struct audit_context *context)
 {
       struct audit_aux_data *aux;
@@ -868,21 +897,29 @@ int audit_alloc(struct task_struct *tsk)
 {
       struct audit_context *context;
       enum audit_state     state;
-       char *key = NULL;
+       struct audit_keys keys[AUDIT_MAX_KEYS];
+       int i, key_count = 0;
+
+       memset(&keys, 0, AUDIT_MAX_KEYS * sizeof(struct audit_keys));

       if (likely(!audit_ever_enabled))
               return 0; /* Return if not auditing. */

-       state = audit_filter_task(tsk, &key);
+       state = audit_filter_task(tsk, keys, &key_count);
       if (likely(state == AUDIT_DISABLED))
               return 0;

       if (!(context = audit_alloc_context(state))) {
-               kfree(key);
+               for (i = 0; i < key_count; i++)
+                       kfree(keys[i].key);
               audit_log_lost("out of memory in audit_alloc");
               return -ENOMEM;
       }
-       context->filterkey = key;
+       context->key_count = key_count;
+       for (i = 0; i < key_count; i++) {
+               context->keys[i].type = keys[i].type;
+               context->keys[i].key = keys[i].key;
+       }

       tsk->audit_context  = context;
       set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
@@ -907,7 +944,7 @@ static inline void audit_free_context(struct
audit_context *context)
               unroll_tree_refs(context, NULL, 0);
               free_tree_refs(context);
               audit_free_aux(context);
-               kfree(context->filterkey);
+               audit_free_keys(context);
               kfree(context->sockaddr);
               kfree(context);
               context  = previous;
@@ -1369,9 +1406,12 @@ static void audit_log_exit(struct audit_context
*context, struct task_struct *ts
                 context->egid, context->sgid, context->fsgid, tty,
                 tsk->sessionid);

-
       audit_log_task_info(ab, tsk);
-       audit_log_key(ab, context->filterkey);
+
+       for (i = 0; i < context->key_count; i++)
+               audit_log_key(ab, context->keys[i].type, context->keys[i].key);
+       if (!i)
+               audit_log_key(ab, 0, NULL);
       audit_log_end(ab);

       for (aux = context->aux; aux; aux = aux->next) {
@@ -1646,6 +1686,8 @@ void audit_finish_fork(struct task_struct *child)
 {
       struct audit_context *ctx = current->audit_context;
       struct audit_context *p = child->audit_context;
+       int i;
+
       if (!p || !ctx)
               return;
       if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT)
@@ -1656,7 +1698,10 @@ void audit_finish_fork(struct task_struct *child)
       p->ctime = ctx->ctime;
       p->dummy = ctx->dummy;
       p->in_syscall = ctx->in_syscall;
-       p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL);
+       for (i = 0; i < ctx->key_count; i++) {
+               p->keys[i].type = ctx->keys[i].type;
+               p->keys[i].key = kstrdup(ctx->keys[i].key, GFP_KERNEL);
+       }
       p->ppid = current->pid;
       p->prio = ctx->prio;
       p->current_state = ctx->current_state;
@@ -1708,10 +1753,8 @@ void audit_syscall_exit(int valid, long return_code)
               context->sockaddr_len = 0;
               context->type = 0;
               context->fds[0] = -1;
-               if (context->state != AUDIT_RECORD_CONTEXT) {
-                       kfree(context->filterkey);
-                       context->filterkey = NULL;
-               }
+               if (context->state != AUDIT_RECORD_CONTEXT)
+                       audit_free_keys(context);
               tsk->audit_context = context;
       }
 }
--
1.6.4.4





User space audit patch:

Index: src/auditctl.c
===================================================================
--- src/auditctl.c      (revision 392)
+++ src/auditctl.c      (working copy)
@@ -73,6 +73,7 @@
 /* External vars */
 extern int audit_archadded;
 extern int audit_syscalladded;
+extern int audit_keyadded;
 extern unsigned int audit_elf;
 extern int audit_permadded;

@@ -84,6 +85,7 @@
 {
       list_requested = 0;
       audit_syscalladded = 0;
+       audit_keyadded = 0;
       audit_permadded = 0;
       audit_archadded = 0;
       audit_elf = 0;
@@ -818,6 +820,9 @@
                                                       AUDIT_MAX_KEY_LEN) {
                       fprintf(stderr, "key option exceeds size limit\n");
                       retval = -1;
+               } else if (audit_keyadded >= AUDIT_MAX_KEYS) {
+                       fprintf(stderr, "too many kyes (ctl)\n");
+                       retval = -1;
               } else {
                       if (strncmp(optarg, "ids-", 4) == 0) {
                               if (check_ids_key(optarg)) {
@@ -834,6 +839,7 @@
                       }
                       strncat(key, optarg, keylen);
                       keylen = AUDIT_MAX_KEY_LEN - strlen(key);
+                       audit_keyadded++;
               }
               break;
       case 'p':
Index: lib/libaudit.h
===================================================================
--- lib/libaudit.h      (revision 392)
+++ lib/libaudit.h      (working copy)
@@ -203,6 +203,9 @@
 /* This is related to the filterkey patch */
 #define AUDIT_KEY_SEPARATOR 0x01

+/* Max number of audit keys */
+#define AUDIT_MAX_KEYS         8
+
 /* These are used in filter control */
 #define AUDIT_FILTER_EXCLUDE   AUDIT_FILTER_TYPE
 #define AUDIT_FILTER_MASK      0x07    /* Mask to get actual filter */
Index: lib/errormsg.h
===================================================================
--- lib/errormsg.h      (revision 392)
+++ lib/errormsg.h      (working copy)
@@ -54,5 +54,6 @@
    { -19,    0,    "Key field needs a watch or syscall given prior to it" },
    { -20,    2,    "-F missing value after operation for" },
    { -21,    2,    "-F value should be number for" },
-    { -22,    2,    "-F missing field name before operator for" }
+    { -22,    2,    "-F missing field name before operator for" },
+    { -23,    2,    "too many keys" }
 };
Index: lib/libaudit.c
===================================================================
--- lib/libaudit.c      (revision 392)
+++ lib/libaudit.c      (working copy)
@@ -80,6 +80,7 @@
 int audit_permadded hidden = 0;
 int audit_archadded hidden = 0;
 int audit_syscalladded hidden = 0;
+int audit_keyadded hidden = 0;
 unsigned int audit_elf hidden = 0U;
 static struct libaudit_conf config;

@@ -943,14 +944,18 @@
               case AUDIT_SUBJ_SEN:
               case AUDIT_SUBJ_CLR:
               case AUDIT_FILTERKEY:
-                       if (field == AUDIT_FILTERKEY &&
!(audit_syscalladded || audit_permadded))
-                                return -19;
                       vlen = strlen(v);
-                       if (field == AUDIT_FILTERKEY &&
-                                       vlen > AUDIT_MAX_KEY_LEN)
+                       if (field == AUDIT_FILTERKEY) {
+                               if (!(audit_syscalladded || audit_permadded))
+                                       return -19;
+                               else if (vlen > AUDIT_MAX_KEY_LEN)
+                                       return -11;
+                               else if (audit_keyadded >= AUDIT_MAX_KEYS)
+                                       return -23;
+                               audit_keyadded++;
+                       } else if (vlen > PATH_MAX) {
                               return -11;
-                       else if (vlen > PATH_MAX)
-                               return -11;
+                       }
                       rule->values[rule->field_count] = vlen;
                       offset = rule->buflen;
                       rule->buflen += vlen;




More information about the Linux-audit mailing list