On 15-10-19 14:21:03, Mimi Zohar wrote:
> On Fri, 2015-10-16 at 22:31 +0300, Petko Manolov wrote:
> > IMA policy can now be updated multiple times.  The new rules get appended
> > to the original policy.  Have in mind that the rules are scanned in FIFO
> > order so be careful when you add new ones.
> > 
> > The mutex locks are replaced with RCU, which should lead to faster policy
> > traversals.  The new rules are first appended to a temporary list, which
> > on error gets released without disturbing the normal IMA operations.
> 
> There was no need for locking in the original version.  This comment should 
> be 
> included in a change log to reflect different versions of the patch.

Yep, a message change is in order.


                Petko


> > Signed-off-by: Petko Manolov <pet...@mip-labs.com> ---
> >  security/integrity/ima/Kconfig      | 14 ++++++++
> >  security/integrity/ima/ima_fs.c     | 13 +++++++
> >  security/integrity/ima/ima_policy.c | 70 
> > +++++++++++++++++++++++++------------
> >  3 files changed, 74 insertions(+), 23 deletions(-)
> > 
> > diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> > index df30334..15264b7 100644
> > --- a/security/integrity/ima/Kconfig
> > +++ b/security/integrity/ima/Kconfig
> > @@ -107,6 +107,20 @@ config IMA_DEFAULT_HASH
> >     default "sha512" if IMA_DEFAULT_HASH_SHA512
> >     default "wp512" if IMA_DEFAULT_HASH_WP512
> > 
> > +config IMA_WRITE_POLICY
> > +   bool "Enable multiple writes to the IMA policy"
> > +   depends on IMA
> > +   default n
> > +   help
> > +     IMA policy can now be updated multiple times.  The new rules get
> > +     appended to the original policy.  Have in mind that the rules are
> > +     scanned in FIFO order so be careful when you add new ones.
> > +
> > +     WARNING: Potential security hole - should be used with care in
> > +     production-grade kernels!
> > +
> > +     If unsure, say N.
> > +
> >  config IMA_APPRAISE
> >     bool "Appraise integrity measurements"
> >     depends on IMA
> > diff --git a/security/integrity/ima/ima_fs.c 
> > b/security/integrity/ima/ima_fs.c
> > index 816d175..a3cf5c0 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -25,6 +25,8 @@
> > 
> >  #include "ima.h"
> > 
> > +static DEFINE_MUTEX(ima_write_mutex);
> > +
> >  static int valid_policy = 1;
> >  #define TMPBUFLEN 12
> >  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
> > @@ -261,6 +263,11 @@ static ssize_t ima_write_policy(struct file *file, 
> > const char __user *buf,
> >  {
> >     char *data = NULL;
> >     ssize_t result;
> > +   int res;
> > +
> > +   res = mutex_lock_interruptible(&ima_write_mutex);
> > +   if (res)
> > +           return res;
> > 
> >     if (datalen >= PAGE_SIZE)
> >             datalen = PAGE_SIZE - 1;
> > @@ -286,6 +293,8 @@ out:
> >     if (result < 0)
> >             valid_policy = 0;
> >     kfree(data);
> > +   mutex_unlock(&ima_write_mutex);
> > +
> >     return result;
> >  }
> > 
> > @@ -337,8 +346,12 @@ static int ima_release_policy(struct inode *inode, 
> > struct file *file)
> >             return 0;
> >     }
> >     ima_update_policy();
> > +#ifndef    CONFIG_IMA_WRITE_POLICY
> >     securityfs_remove(ima_policy);
> >     ima_policy = NULL;
> > +#else
> > +   clear_bit(IMA_FS_BUSY, &ima_fs_flags);
> > +#endif
> >     return 0;
> >  }
> > 
> > diff --git a/security/integrity/ima/ima_policy.c 
> > b/security/integrity/ima/ima_policy.c
> > index 3997e20..7ace4e4 100644
> > --- a/security/integrity/ima/ima_policy.c
> > +++ b/security/integrity/ima/ima_policy.c
> > @@ -16,6 +16,7 @@
> >  #include <linux/magic.h>
> >  #include <linux/parser.h>
> >  #include <linux/slab.h>
> > +#include <linux/rculist.h>
> >  #include <linux/genhd.h>
> > 
> >  #include "ima.h"
> > @@ -135,11 +136,11 @@ static struct ima_rule_entry default_appraise_rules[] 
> > = {
> > 
> >  static LIST_HEAD(ima_default_rules);
> >  static LIST_HEAD(ima_policy_rules);
> > +static LIST_HEAD(ima_temp_rules);
> >  static struct list_head *ima_rules;
> > 
> > -static DEFINE_MUTEX(ima_rules_mutex);
> > -
> >  static int ima_policy __initdata;
> > +
> >  static int __init default_measure_policy_setup(char *str)
> >  {
> >     if (ima_policy)
> > @@ -171,9 +172,8 @@ static int __init default_appraise_policy_setup(char 
> > *str)
> >  __setup("ima_appraise_tcb", default_appraise_policy_setup);
> > 
> >  /*
> > - * Although the IMA policy does not change, the LSM policy can be
> > - * reloaded, leaving the IMA LSM based rules referring to the old,
> > - * stale LSM policy.
> > + * Blocking here is not legal as we hold an RCU lock.  ima_update_policy()
> > + * should make it safe to walk the list at any time.
> >   *
> >   * Update the IMA LSM based rules to reflect the reloaded LSM policy.
> >   * We assume the rules still exist; and BUG_ON() if they don't.
> > @@ -184,7 +184,6 @@ static void ima_lsm_update_rules(void)
> >     int result;
> >     int i;
> > 
> > -   mutex_lock(&ima_rules_mutex);
> >     list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
> >             for (i = 0; i < MAX_LSM_RULES; i++) {
> >                     if (!entry->lsm[i].rule)
> > @@ -196,7 +195,6 @@ static void ima_lsm_update_rules(void)
> >                     BUG_ON(!entry->lsm[i].rule);
> >             }
> >     }
> > -   mutex_unlock(&ima_rules_mutex);
> >  }
> > 
> >  /**
> > @@ -319,9 +317,9 @@ static int get_subaction(struct ima_rule_entry *rule, 
> > int func)
> >   * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
> >   * conditions.
> >   *
> > - * (There is no need for locking when walking the policy list,
> > - * as elements in the list are never deleted, nor does the list
> > - * change.)
> > + * Since the IMA policy may be updated multiple times we need to lock the
> > + * list when walking it.  Reads are many orders of magnitude more numerous
> > + * than writes so ima_match_policy() is classical RCU candidate.
> >   */
> >  int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
> >                  int flags)
> > @@ -329,7 +327,8 @@ int ima_match_policy(struct inode *inode, enum 
> > ima_hooks func, int mask,
> >     struct ima_rule_entry *entry;
> >     int action = 0, actmask = flags | (flags << 1);
> > 
> > -   list_for_each_entry(entry, ima_rules, list) {
> > +   rcu_read_lock();
> > +   list_for_each_entry_rcu(entry, ima_rules, list) {
> > 
> >             if (!(entry->action & actmask))
> >                     continue;
> > @@ -351,6 +350,7 @@ int ima_match_policy(struct inode *inode, enum 
> > ima_hooks func, int mask,
> >             if (!actmask)
> >                     break;
> >     }
> > +   rcu_read_unlock();
> > 
> >     return action;
> >  }
> > @@ -365,7 +365,6 @@ void ima_update_policy_flag(void)
> >  {
> >     struct ima_rule_entry *entry;
> > 
> > -   ima_policy_flag = 0;
> >     list_for_each_entry(entry, ima_rules, list) {
> >             if (entry->action & IMA_DO_MASK)
> >                     ima_policy_flag |= entry->action;
> > @@ -419,12 +418,36 @@ void __init ima_init_policy(void)
> >   * ima_update_policy - update default_rules with new measure rules
> >   *
> >   * Called on file .release to update the default rules with a complete new
> > - * policy.  Once updated, the policy is locked, no additional rules can be
> > - * added to the policy.
> > + * policy.  What we do here is to splice ima_policy_rules and 
> > ima_temp_rules so
> > + * they make a queue.  The policy may be updated multiple times and this 
> > is the
> > + * RCU updater.
> > + *
> > + * Policy rules are never deleted so ima_policy_flag gets zeroed only once 
> > when
> > + * we switch from the default policy to user defined.
> >   */
> >  void ima_update_policy(void)
> >  {
> > -   ima_rules = &ima_policy_rules;
> > +   struct list_head *first, *last, *policy;
> > +
> > +   /* append current policy with the new rules */
> > +   first = (&ima_temp_rules)->next;
> > +   last = (&ima_temp_rules)->prev;
> > +   policy = &ima_policy_rules;
> > +
> > +   synchronize_rcu();
> > +
> > +   last->next = policy;
> > +   rcu_assign_pointer(list_next_rcu(policy->prev), first);
> > +   first->prev = policy->prev;
> > +   policy->prev = last;
> > +
> > +   /* prepare for the next policy rules addition */
> > +   INIT_LIST_HEAD(&ima_temp_rules);
> > +
> > +   if (ima_rules != policy) {
> > +           ima_policy_flag = 0;
> > +           ima_rules = policy;
> > +   }
> >     ima_update_policy_flag();
> >  }
> > 
> > @@ -746,7 +769,7 @@ static int ima_parse_rule(char *rule, struct 
> > ima_rule_entry *entry)
> >   * ima_parse_add_rule - add a rule to ima_policy_rules
> >   * @rule - ima measurement policy rule
> >   *
> > - * Uses a mutex to protect the policy list from multiple concurrent 
> > writers.
> > + * Avoid locking by allowing just one writer at a time in 
> > ima_write_policy()
> >   * Returns the length of the rule parsed, an error code on failure
> >   */
> >  ssize_t ima_parse_add_rule(char *rule)
> > @@ -782,26 +805,27 @@ ssize_t ima_parse_add_rule(char *rule)
> >             return result;
> >     }
> > 
> > -   mutex_lock(&ima_rules_mutex);
> > -   list_add_tail(&entry->list, &ima_policy_rules);
> > -   mutex_unlock(&ima_rules_mutex);
> > +   list_add_tail(&entry->list, &ima_temp_rules);
> > 
> >     return len;
> >  }
> > 
> > -/* ima_delete_rules called to cleanup invalid policy */
> > +/**
> > + * ima_delete_rules() called to cleanup invalid in-flight policy.
> > + * We don't need locking as we operate on the temp list, which is
> > + * different from the active one.  There is also only one user of
> > + * ima_delete_rules() at a time.
> > + */
> >  void ima_delete_rules(void)
> >  {
> >     struct ima_rule_entry *entry, *tmp;
> >     int i;
> > 
> > -   mutex_lock(&ima_rules_mutex);
> > -   list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
> > +   list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
> >             for (i = 0; i < MAX_LSM_RULES; i++)
> >                     kfree(entry->lsm[i].args_p);
> > 
> >             list_del(&entry->list);
> >             kfree(entry);
> >     }
> > -   mutex_unlock(&ima_rules_mutex);
> >  }
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe 
> linux-security-module" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to