These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / kernel / auditfilter.c
index 72e1660..b8ff9e1 100644 (file)
  * Locking model:
  *
  * audit_filter_mutex:
- *             Synchronizes writes and blocking reads of audit's filterlist
- *             data.  Rcu is used to traverse the filterlist and access
- *             contents of structs audit_entry, audit_watch and opaque
- *             LSM rules during filtering.  If modified, these structures
- *             must be copied and replace their counterparts in the filterlist.
- *             An audit_parent struct is not accessed during filtering, so may
- *             be written directly provided audit_filter_mutex is held.
+ *             Synchronizes writes and blocking reads of audit's filterlist
+ *             data.  Rcu is used to traverse the filterlist and access
+ *             contents of structs audit_entry, audit_watch and opaque
+ *             LSM rules during filtering.  If modified, these structures
+ *             must be copied and replace their counterparts in the filterlist.
+ *             An audit_parent struct is not accessed during filtering, so may
+ *             be written directly provided audit_filter_mutex is held.
  */
 
 /* Audit filter lists, defined in <linux/audit.h> */
@@ -405,6 +405,12 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
                if (f->val > AUDIT_MAX_FIELD_COMPARE)
                        return -EINVAL;
                break;
+       case AUDIT_EXE:
+               if (f->op != Audit_equal)
+                       return -EINVAL;
+               if (entry->rule.listnr != AUDIT_FILTER_EXIT)
+                       return -EINVAL;
+               break;
        };
        return 0;
 }
@@ -419,6 +425,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
        size_t remain = datasz - sizeof(struct audit_rule_data);
        int i;
        char *str;
+       struct audit_fsnotify_mark *audit_mark;
 
        entry = audit_to_entry_common(data);
        if (IS_ERR(entry))
@@ -539,6 +546,24 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                        entry->rule.buflen += f->val;
                        entry->rule.filterkey = str;
                        break;
+               case AUDIT_EXE:
+                       if (entry->rule.exe || f->val > PATH_MAX)
+                               goto exit_free;
+                       str = audit_unpack_string(&bufp, &remain, f->val);
+                       if (IS_ERR(str)) {
+                               err = PTR_ERR(str);
+                               goto exit_free;
+                       }
+                       entry->rule.buflen += f->val;
+
+                       audit_mark = audit_alloc_mark(&entry->rule, str, f->val);
+                       if (IS_ERR(audit_mark)) {
+                               kfree(str);
+                               err = PTR_ERR(audit_mark);
+                               goto exit_free;
+                       }
+                       entry->rule.exe = audit_mark;
+                       break;
                }
        }
 
@@ -549,10 +574,10 @@ exit_nofree:
        return entry;
 
 exit_free:
-       if (entry->rule.watch)
-               audit_put_watch(entry->rule.watch); /* matches initial get */
        if (entry->rule.tree)
                audit_put_tree(entry->rule.tree); /* that's the temporary one */
+       if (entry->rule.exe)
+               audit_remove_mark(entry->rule.exe); /* that's the template one */
        audit_free_rule(entry);
        return ERR_PTR(err);
 }
@@ -617,6 +642,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
                        data->buflen += data->values[i] =
                                audit_pack_string(&bufp, krule->filterkey);
                        break;
+               case AUDIT_EXE:
+                       data->buflen += data->values[i] =
+                               audit_pack_string(&bufp, audit_mark_path(krule->exe));
+                       break;
                case AUDIT_LOGINUID_SET:
                        if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) {
                                data->fields[i] = AUDIT_LOGINUID;
@@ -680,6 +709,12 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
                        if (strcmp(a->filterkey, b->filterkey))
                                return 1;
                        break;
+               case AUDIT_EXE:
+                       /* both paths exist based on above type compare */
+                       if (strcmp(audit_mark_path(a->exe),
+                                  audit_mark_path(b->exe)))
+                               return 1;
+                       break;
                case AUDIT_UID:
                case AUDIT_EUID:
                case AUDIT_SUID:
@@ -801,8 +836,14 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old)
                                err = -ENOMEM;
                        else
                                new->filterkey = fk;
+                       break;
+               case AUDIT_EXE:
+                       err = audit_dupe_exe(new, old);
+                       break;
                }
                if (err) {
+                       if (new->exe)
+                               audit_remove_mark(new->exe);
                        audit_free_rule(entry);
                        return ERR_PTR(err);
                }
@@ -863,7 +904,7 @@ static inline int audit_add_rule(struct audit_entry *entry)
        struct audit_watch *watch = entry->rule.watch;
        struct audit_tree *tree = entry->rule.tree;
        struct list_head *list;
-       int err;
+       int err = 0;
 #ifdef CONFIG_AUDITSYSCALL
        int dont_count = 0;
 
@@ -881,7 +922,7 @@ static inline int audit_add_rule(struct audit_entry *entry)
                /* normally audit_add_tree_rule() will free it on failure */
                if (tree)
                        audit_put_tree(tree);
-               goto error;
+               return err;
        }
 
        if (watch) {
@@ -895,14 +936,14 @@ static inline int audit_add_rule(struct audit_entry *entry)
                         */
                        if (tree)
                                audit_put_tree(tree);
-                       goto error;
+                       return err;
                }
        }
        if (tree) {
                err = audit_add_tree_rule(&entry->rule);
                if (err) {
                        mutex_unlock(&audit_filter_mutex);
-                       goto error;
+                       return err;
                }
        }
 
@@ -933,19 +974,13 @@ static inline int audit_add_rule(struct audit_entry *entry)
 #endif
        mutex_unlock(&audit_filter_mutex);
 
-       return 0;
-
-error:
-       if (watch)
-               audit_put_watch(watch); /* tmp watch, matches initial get */
        return err;
 }
 
 /* Remove an existing rule from filterlist. */
-static inline int audit_del_rule(struct audit_entry *entry)
+int audit_del_rule(struct audit_entry *entry)
 {
        struct audit_entry  *e;
-       struct audit_watch *watch = entry->rule.watch;
        struct audit_tree *tree = entry->rule.tree;
        struct list_head *list;
        int ret = 0;
@@ -961,7 +996,6 @@ static inline int audit_del_rule(struct audit_entry *entry)
        mutex_lock(&audit_filter_mutex);
        e = audit_find_rule(entry, &list);
        if (!e) {
-               mutex_unlock(&audit_filter_mutex);
                ret = -ENOENT;
                goto out;
        }
@@ -972,9 +1006,8 @@ static inline int audit_del_rule(struct audit_entry *entry)
        if (e->rule.tree)
                audit_remove_tree_rule(&e->rule);
 
-       list_del_rcu(&e->list);
-       list_del(&e->rule.list);
-       call_rcu(&e->rcu, audit_free_rule_rcu);
+       if (e->rule.exe)
+               audit_remove_mark_rule(&e->rule);
 
 #ifdef CONFIG_AUDITSYSCALL
        if (!dont_count)
@@ -983,11 +1016,14 @@ static inline int audit_del_rule(struct audit_entry *entry)
        if (!audit_match_signal(entry))
                audit_signals--;
 #endif
-       mutex_unlock(&audit_filter_mutex);
+
+       list_del_rcu(&e->list);
+       list_del(&e->rule.list);
+       call_rcu(&e->rcu, audit_free_rule_rcu);
 
 out:
-       if (watch)
-               audit_put_watch(watch); /* match initial get */
+       mutex_unlock(&audit_filter_mutex);
+
        if (tree)
                audit_put_tree(tree);   /* that's the temporary one */
 
@@ -1077,8 +1113,11 @@ int audit_rule_change(int type, __u32 portid, int seq, void *data,
                WARN_ON(1);
        }
 
-       if (err || type == AUDIT_DEL_RULE)
+       if (err || type == AUDIT_DEL_RULE) {
+               if (entry->rule.exe)
+                       audit_remove_mark(entry->rule.exe);
                audit_free_rule(entry);
+       }
 
        return err;
 }
@@ -1370,6 +1409,8 @@ static int update_lsm_rule(struct audit_krule *r)
                return 0;
 
        nentry = audit_dupe_rule(r);
+       if (entry->rule.exe)
+               audit_remove_mark(entry->rule.exe);
        if (IS_ERR(nentry)) {
                /* save the first error encountered for the
                 * return value */