ima: re-initialize IMA policy LSM info

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.  This patch updates the IMA LSM based rules
to reflect the reloaded LSM policy.

Reported-by: Sven Vermeulen <sven.vermeulen@siphos.be>
tested-by: Sven Vermeulen <sven.vermeulen@siphos.be>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Cc: Eric Paris <eparis@parisplace.org>
Cc: Casey Schaufler <casey@schaufler-ca.com>
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index af7d182..70f888d 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -49,6 +49,7 @@
 	kuid_t fowner;
 	struct {
 		void *rule;	/* LSM file metadata specific */
+		void *args_p;	/* audit value */
 		int type;	/* audit type */
 	} lsm[MAX_LSM_RULES];
 };
@@ -119,6 +120,35 @@
 }
 __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.
+ *
+ * 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.
+ */
+static void ima_lsm_update_rules(void)
+{
+	struct ima_rule_entry *entry, *tmp;
+	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)
+				continue;
+			result = security_filter_rule_init(entry->lsm[i].type,
+							   Audit_equal,
+							   entry->lsm[i].args_p,
+							   &entry->lsm[i].rule);
+			BUG_ON(!entry->lsm[i].rule);
+		}
+	}
+	mutex_unlock(&ima_rules_mutex);
+}
+
 /**
  * ima_match_rules - determine whether an inode matches the measure rule.
  * @rule: a pointer to a rule
@@ -149,10 +179,11 @@
 	for (i = 0; i < MAX_LSM_RULES; i++) {
 		int rc = 0;
 		u32 osid, sid;
+		int retried = 0;
 
 		if (!rule->lsm[i].rule)
 			continue;
-
+retry:
 		switch (i) {
 		case LSM_OBJ_USER:
 		case LSM_OBJ_ROLE:
@@ -176,6 +207,11 @@
 		default:
 			break;
 		}
+		if ((rc < 0) && (!retried)) {
+			retried = 1;
+			ima_lsm_update_rules();
+			goto retry;
+		} 
 		if (!rc)
 			return false;
 	}
@@ -306,19 +342,27 @@
 };
 
 static int ima_lsm_rule_init(struct ima_rule_entry *entry,
-			     char *args, int lsm_rule, int audit_type)
+			     substring_t *args, int lsm_rule, int audit_type)
 {
 	int result;
 
 	if (entry->lsm[lsm_rule].rule)
 		return -EINVAL;
 
+	entry->lsm[lsm_rule].args_p = match_strdup(args);
+	if (!entry->lsm[lsm_rule].args_p)
+		return -ENOMEM;
+
 	entry->lsm[lsm_rule].type = audit_type;
 	result = security_filter_rule_init(entry->lsm[lsm_rule].type,
-					   Audit_equal, args,
+					   Audit_equal,
+					   entry->lsm[lsm_rule].args_p,
 					   &entry->lsm[lsm_rule].rule);
-	if (!entry->lsm[lsm_rule].rule)
+	if (!entry->lsm[lsm_rule].rule) {
+		kfree(entry->lsm[lsm_rule].args_p);
 		return -EINVAL;
+	}
+
 	return result;
 }
 
@@ -481,37 +525,37 @@
 			break;
 		case Opt_obj_user:
 			ima_log_string(ab, "obj_user", args[0].from);
-			result = ima_lsm_rule_init(entry, args[0].from,
+			result = ima_lsm_rule_init(entry, args,
 						   LSM_OBJ_USER,
 						   AUDIT_OBJ_USER);
 			break;
 		case Opt_obj_role:
 			ima_log_string(ab, "obj_role", args[0].from);
-			result = ima_lsm_rule_init(entry, args[0].from,
+			result = ima_lsm_rule_init(entry, args,
 						   LSM_OBJ_ROLE,
 						   AUDIT_OBJ_ROLE);
 			break;
 		case Opt_obj_type:
 			ima_log_string(ab, "obj_type", args[0].from);
-			result = ima_lsm_rule_init(entry, args[0].from,
+			result = ima_lsm_rule_init(entry, args,
 						   LSM_OBJ_TYPE,
 						   AUDIT_OBJ_TYPE);
 			break;
 		case Opt_subj_user:
 			ima_log_string(ab, "subj_user", args[0].from);
-			result = ima_lsm_rule_init(entry, args[0].from,
+			result = ima_lsm_rule_init(entry, args,
 						   LSM_SUBJ_USER,
 						   AUDIT_SUBJ_USER);
 			break;
 		case Opt_subj_role:
 			ima_log_string(ab, "subj_role", args[0].from);
-			result = ima_lsm_rule_init(entry, args[0].from,
+			result = ima_lsm_rule_init(entry, args,
 						   LSM_SUBJ_ROLE,
 						   AUDIT_SUBJ_ROLE);
 			break;
 		case Opt_subj_type:
 			ima_log_string(ab, "subj_type", args[0].from);
-			result = ima_lsm_rule_init(entry, args[0].from,
+			result = ima_lsm_rule_init(entry, args,
 						   LSM_SUBJ_TYPE,
 						   AUDIT_SUBJ_TYPE);
 			break;
@@ -589,9 +633,13 @@
 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) {
+		for (i = 0; i < MAX_LSM_RULES; i++)
+			kfree(entry->lsm[i].args_p);
+
 		list_del(&entry->list);
 		kfree(entry);
 	}