Merge branch 'ima-kexec-fixes' into next-integrity
authorMimi Zohar <zohar@linux.ibm.com>
Wed, 10 Feb 2021 21:34:06 +0000 (16:34 -0500)
committerMimi Zohar <zohar@linux.ibm.com>
Wed, 10 Feb 2021 21:34:06 +0000 (16:34 -0500)
17 files changed:
Documentation/ABI/testing/ima_policy
Documentation/admin-guide/kernel-parameters.txt
include/linux/ima.h
security/integrity/evm/evm_crypto.c
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_asymmetric_keys.c
security/integrity/ima/ima_init.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/ima/ima_queue_keys.c
security/selinux/Makefile
security/selinux/ima.c [new file with mode: 0644]
security/selinux/include/ima.h [new file with mode: 0644]
security/selinux/include/security.h
security/selinux/ss/services.c

index e35263f..bc8e1cb 100644 (file)
@@ -32,7 +32,7 @@ Description:
                        func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK]MODULE_CHECK]
                                [FIRMWARE_CHECK]
                                [KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
-                               [KEXEC_CMDLINE] [KEY_CHECK]
+                               [KEXEC_CMDLINE] [KEY_CHECK] [CRITICAL_DATA]
                        mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
                               [[^]MAY_EXEC]
                        fsmagic:= hex value
@@ -52,6 +52,9 @@ Description:
                        template:= name of a defined IMA template type
                        (eg, ima-ng). Only valid when action is "measure".
                        pcr:= decimal value
+                       label:= [selinux]|[kernel_info]|[data_label]
+                       data_label:= a unique string used for grouping and limiting critical data.
+                       For example, "selinux" to measure critical data for SELinux.
 
                  default policy:
                        # PROC_SUPER_MAGIC
index 9e3cdb2..65a0c4c 100644 (file)
        ima_policy=     [IMA]
                        The builtin policies to load during IMA setup.
                        Format: "tcb | appraise_tcb | secure_boot |
-                                fail_securely"
+                                fail_securely | critical_data"
 
                        The "tcb" policy measures all programs exec'd, files
                        mmap'd for exec, and all files opened with the read
                        filesystems with the SB_I_UNVERIFIABLE_SIGNATURE
                        flag.
 
+                       The "critical_data" policy measures kernel integrity
+                       critical data.
+
        ima_tcb         [IMA] Deprecated.  Use ima_policy= instead.
                        Load a policy which meets the needs of the Trusted
                        Computing Base.  This means IMA will measure all
index 7db9cca..2ac834b 100644 (file)
@@ -31,6 +31,10 @@ extern void ima_post_path_mknod(struct dentry *dentry);
 extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
 extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size);
 extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
+extern void ima_measure_critical_data(const char *event_label,
+                                     const char *event_name,
+                                     const void *buf, size_t buf_len,
+                                     bool hash);
 
 #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
 extern void ima_appraise_parse_cmdline(void);
@@ -128,6 +132,12 @@ static inline int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size
 }
 
 static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
+
+static inline void ima_measure_critical_data(const char *event_label,
+                                            const char *event_name,
+                                            const void *buf, size_t buf_len,
+                                            bool hash) {}
+
 #endif /* CONFIG_IMA */
 
 #ifndef CONFIG_IMA_KEXEC
index 168c3b7..a6dd47e 100644 (file)
@@ -73,7 +73,7 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo)
 {
        long rc;
        const char *algo;
-       struct crypto_shash **tfm, *tmp_tfm;
+       struct crypto_shash **tfm, *tmp_tfm = NULL;
        struct shash_desc *desc;
 
        if (type == EVM_XATTR_HMAC) {
@@ -118,13 +118,16 @@ unlock:
 alloc:
        desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm),
                        GFP_KERNEL);
-       if (!desc)
+       if (!desc) {
+               crypto_free_shash(tmp_tfm);
                return ERR_PTR(-ENOMEM);
+       }
 
        desc->tfm = *tfm;
 
        rc = crypto_shash_init(desc);
        if (rc) {
+               crypto_free_shash(tmp_tfm);
                kfree(desc);
                return ERR_PTR(rc);
        }
index 8e8b1e3..aa31247 100644 (file)
@@ -201,6 +201,7 @@ static inline unsigned int ima_hash_key(u8 *digest)
        hook(POLICY_CHECK, policy)                      \
        hook(KEXEC_CMDLINE, kexec_cmdline)              \
        hook(KEY_CHECK, key)                            \
+       hook(CRITICAL_DATA, critical_data)              \
        hook(MAX_CHECK, none)
 
 #define __ima_hook_enumify(ENUM, str)  ENUM,
@@ -256,7 +257,7 @@ static inline void ima_process_queued_keys(void) {}
 int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
                   int mask, enum ima_hooks func, int *pcr,
                   struct ima_template_desc **template_desc,
-                  const char *keyring);
+                  const char *func_data);
 int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
                            struct file *file, void *buf, loff_t size,
@@ -268,7 +269,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
                           struct ima_template_desc *template_desc);
 void process_buffer_measurement(struct inode *inode, const void *buf, int size,
                                const char *eventname, enum ima_hooks func,
-                               int pcr, const char *keyring);
+                               int pcr, const char *func_data,
+                               bool buf_hash);
 void ima_audit_measurement(struct integrity_iint_cache *iint,
                           const unsigned char *filename);
 int ima_alloc_init_template(struct ima_event_data *event_data,
@@ -284,7 +286,7 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
 int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
                     enum ima_hooks func, int mask, int flags, int *pcr,
                     struct ima_template_desc **template_desc,
-                    const char *keyring);
+                    const char *func_data);
 void ima_init_policy(void);
 void ima_update_policy(void);
 void ima_update_policy_flag(void);
index 4f39fb9..1dd70dc 100644 (file)
@@ -170,13 +170,13 @@ err_out:
  * @func: caller identifier
  * @pcr: pointer filled in if matched measure policy sets pcr=
  * @template_desc: pointer filled in if matched measure policy sets template=
- * @keyring: keyring name used to determine the action
+ * @func_data: func specific data, may be NULL
  *
  * The policy is defined in terms of keypairs:
  *             subj=, obj=, type=, func=, mask=, fsmagic=
  *     subj,obj, and type: are LSM specific.
  *     func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
- *     | KEXEC_CMDLINE | KEY_CHECK
+ *     | KEXEC_CMDLINE | KEY_CHECK | CRITICAL_DATA
  *     mask: contains the permission mask
  *     fsmagic: hex value
  *
@@ -186,14 +186,14 @@ err_out:
 int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
                   int mask, enum ima_hooks func, int *pcr,
                   struct ima_template_desc **template_desc,
-                  const char *keyring)
+                  const char *func_data)
 {
        int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
 
        flags &= ima_policy_flag;
 
        return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
-                               template_desc, keyring);
+                               template_desc, func_data);
 }
 
 /*
index 8361941..46ffa38 100644 (file)
@@ -352,7 +352,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
                if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
                        process_buffer_measurement(NULL, digest, digestsize,
                                                   "blacklisted-hash", NONE,
-                                                  pcr, NULL);
+                                                  pcr, NULL, false);
        }
 
        return rc;
index 1c68c50..a740957 100644 (file)
@@ -60,5 +60,5 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
         */
        process_buffer_measurement(NULL, payload, payload_len,
                                   keyring->description, KEY_CHECK, 0,
-                                  keyring->description);
+                                  keyring->description, false);
 }
index 4902fe7..6e87429 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/ima.h>
+#include <generated/utsrelease.h>
 
 #include "ima.h"
 
@@ -147,5 +149,8 @@ int __init ima_init(void)
 
        ima_init_key_queue();
 
+       ima_measure_critical_data("kernel_info", "kernel_version",
+                                 UTS_RELEASE, strlen(UTS_RELEASE), false);
+
        return rc;
 }
index f87cb29..6a42984 100644 (file)
@@ -809,20 +809,22 @@ int ima_post_load_data(char *buf, loff_t size,
 }
 
 /*
- * process_buffer_measurement - Measure the buffer to ima log.
+ * process_buffer_measurement - Measure the buffer or the buffer data hash
  * @inode: inode associated with the object being measured (NULL for KEY_CHECK)
  * @buf: pointer to the buffer that needs to be added to the log.
  * @size: size of buffer(in bytes).
  * @eventname: event name to be used for the buffer entry.
  * @func: IMA hook
  * @pcr: pcr to extend the measurement
- * @keyring: keyring name to determine the action to be performed
+ * @func_data: func specific data, may be NULL
+ * @buf_hash: measure buffer data hash
  *
- * Based on policy, the buffer is measured into the ima log.
+ * Based on policy, either the buffer data or buffer data hash is measured
  */
 void process_buffer_measurement(struct inode *inode, const void *buf, int size,
                                const char *eventname, enum ima_hooks func,
-                               int pcr, const char *keyring)
+                               int pcr, const char *func_data,
+                               bool buf_hash)
 {
        int ret = 0;
        const char *audit_cause = "ENOMEM";
@@ -837,6 +839,8 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
                struct ima_digest_data hdr;
                char digest[IMA_MAX_DIGEST_SIZE];
        } hash = {};
+       char digest_hash[IMA_MAX_DIGEST_SIZE];
+       int digest_hash_len = hash_digest_size[ima_hash_algo];
        int violation = 0;
        int action = 0;
        u32 secid;
@@ -861,7 +865,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
        if (func) {
                security_task_getsecid(current, &secid);
                action = ima_get_action(inode, current_cred(), secid, 0, func,
-                                       &pcr, &template, keyring);
+                                       &pcr, &template, func_data);
                if (!(action & IMA_MEASURE))
                        return;
        }
@@ -879,13 +883,27 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
                goto out;
        }
 
+       if (buf_hash) {
+               memcpy(digest_hash, hash.hdr.digest, digest_hash_len);
+
+               ret = ima_calc_buffer_hash(digest_hash, digest_hash_len,
+                                          iint.ima_hash);
+               if (ret < 0) {
+                       audit_cause = "hashing_error";
+                       goto out;
+               }
+
+               event_data.buf = digest_hash;
+               event_data.buf_len = digest_hash_len;
+       }
+
        ret = ima_alloc_init_template(&event_data, &entry, template);
        if (ret < 0) {
                audit_cause = "alloc_entry";
                goto out;
        }
 
-       ret = ima_store_template(entry, violation, NULL, buf, pcr);
+       ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr);
        if (ret < 0) {
                audit_cause = "store_entry";
                ima_free_template_entry(entry);
@@ -920,10 +938,37 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
                return;
 
        process_buffer_measurement(file_inode(f.file), buf, size,
-                                  "kexec-cmdline", KEXEC_CMDLINE, 0, NULL);
+                                  "kexec-cmdline", KEXEC_CMDLINE, 0, NULL,
+                                  false);
        fdput(f);
 }
 
+/**
+ * ima_measure_critical_data - measure kernel integrity critical data
+ * @event_label: unique event label for grouping and limiting critical data
+ * @event_name: event name for the record in the IMA measurement list
+ * @buf: pointer to buffer data
+ * @buf_len: length of buffer data (in bytes)
+ * @hash: measure buffer data hash
+ *
+ * Measure data critical to the integrity of the kernel into the IMA log
+ * and extend the pcr.  Examples of critical data could be various data
+ * structures, policies, and states stored in kernel memory that can
+ * impact the integrity of the system.
+ */
+void ima_measure_critical_data(const char *event_label,
+                              const char *event_name,
+                              const void *buf, size_t buf_len,
+                              bool hash)
+{
+       if (!event_name || !event_label || !buf || !buf_len)
+               return;
+
+       process_buffer_measurement(NULL, buf, buf_len, event_name,
+                                  CRITICAL_DATA, 0, event_label,
+                                  hash);
+}
+
 static int __init init_ima(void)
 {
        int error;
index 823a0c1..9b45d06 100644 (file)
@@ -34,6 +34,7 @@
 #define IMA_PCR                0x0100
 #define IMA_FSNAME     0x0200
 #define IMA_KEYRINGS   0x0400
+#define IMA_LABEL      0x0800
 
 #define UNKNOWN                0
 #define MEASURE                0x0001  /* same as IMA_MEASURE */
@@ -85,6 +86,7 @@ struct ima_rule_entry {
        } lsm[MAX_LSM_RULES];
        char *fsname;
        struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
+       struct ima_rule_opt_list *label; /* Measure data grouped under this label */
        struct ima_template_desc *template;
 };
 
@@ -204,6 +206,10 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
         .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
 };
 
+static struct ima_rule_entry critical_data_rules[] __ro_after_init = {
+       {.action = MEASURE, .func = CRITICAL_DATA, .flags = IMA_FUNC},
+};
+
 /* An array of architecture specific rules */
 static struct ima_rule_entry *arch_policy_entry __ro_after_init;
 
@@ -226,6 +232,7 @@ __setup("ima_tcb", default_measure_policy_setup);
 
 static bool ima_use_appraise_tcb __initdata;
 static bool ima_use_secure_boot __initdata;
+static bool ima_use_critical_data __initdata;
 static bool ima_fail_unverifiable_sigs __ro_after_init;
 static int __init policy_setup(char *str)
 {
@@ -240,6 +247,8 @@ static int __init policy_setup(char *str)
                        ima_use_appraise_tcb = true;
                else if (strcmp(p, "secure_boot") == 0)
                        ima_use_secure_boot = true;
+               else if (strcmp(p, "critical_data") == 0)
+                       ima_use_critical_data = true;
                else if (strcmp(p, "fail_securely") == 0)
                        ima_fail_unverifiable_sigs = true;
                else
@@ -453,30 +462,46 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
 }
 
 /**
- * ima_match_keyring - determine whether the keyring matches the measure rule
+ * ima_match_rule_data - determine whether func_data matches the policy rule
  * @rule: a pointer to a rule
- * @keyring: name of the keyring to match against the measure rule
+ * @func_data: data to match against the measure rule data
  * @cred: a pointer to a credentials structure for user validation
  *
- * Returns true if keyring matches one in the rule, false otherwise.
+ * Returns true if func_data matches one in the rule, false otherwise.
  */
-static bool ima_match_keyring(struct ima_rule_entry *rule,
-                             const char *keyring, const struct cred *cred)
+static bool ima_match_rule_data(struct ima_rule_entry *rule,
+                               const char *func_data,
+                               const struct cred *cred)
 {
+       const struct ima_rule_opt_list *opt_list = NULL;
        bool matched = false;
        size_t i;
 
        if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
                return false;
 
-       if (!rule->keyrings)
-               return true;
+       switch (rule->func) {
+       case KEY_CHECK:
+               if (!rule->keyrings)
+                       return true;
+
+               opt_list = rule->keyrings;
+               break;
+       case CRITICAL_DATA:
+               if (!rule->label)
+                       return true;
+
+               opt_list = rule->label;
+               break;
+       default:
+               return false;
+       }
 
-       if (!keyring)
+       if (!func_data)
                return false;
 
-       for (i = 0; i < rule->keyrings->count; i++) {
-               if (!strcmp(rule->keyrings->items[i], keyring)) {
+       for (i = 0; i < opt_list->count; i++) {
+               if (!strcmp(opt_list->items[i], func_data)) {
                        matched = true;
                        break;
                }
@@ -493,24 +518,30 @@ static bool ima_match_keyring(struct ima_rule_entry *rule,
  * @secid: the secid of the task to be validated
  * @func: LIM hook identifier
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
- * @keyring: keyring name to check in policy for KEY_CHECK func
+ * @func_data: func specific data, may be NULL
  *
  * Returns true on rule match, false on failure.
  */
 static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
                            const struct cred *cred, u32 secid,
                            enum ima_hooks func, int mask,
-                           const char *keyring)
+                           const char *func_data)
 {
        int i;
 
-       if (func == KEY_CHECK) {
-               return (rule->flags & IMA_FUNC) && (rule->func == func) &&
-                      ima_match_keyring(rule, keyring, cred);
-       }
        if ((rule->flags & IMA_FUNC) &&
            (rule->func != func && func != POST_SETATTR))
                return false;
+
+       switch (func) {
+       case KEY_CHECK:
+       case CRITICAL_DATA:
+               return ((rule->func == func) &&
+                       ima_match_rule_data(rule, func_data, cred));
+       default:
+               break;
+       }
+
        if ((rule->flags & IMA_MASK) &&
            (rule->mask != mask && func != POST_SETATTR))
                return false;
@@ -610,8 +641,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
  * @pcr: set the pcr to extend
  * @template_desc: the template that should be used for this rule
- * @keyring: the keyring name, if given, to be used to check in the policy.
- *           keyring can be NULL if func is anything other than KEY_CHECK.
+ * @func_data: func specific data, may be NULL
  *
  * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
  * conditions.
@@ -623,7 +653,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
 int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
                     enum ima_hooks func, int mask, int flags, int *pcr,
                     struct ima_template_desc **template_desc,
-                    const char *keyring)
+                    const char *func_data)
 {
        struct ima_rule_entry *entry;
        int action = 0, actmask = flags | (flags << 1);
@@ -638,7 +668,7 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
                        continue;
 
                if (!ima_match_rules(entry, inode, cred, secid, func, mask,
-                                    keyring))
+                                    func_data))
                        continue;
 
                action |= entry->flags & IMA_ACTION_FLAGS;
@@ -848,6 +878,11 @@ void __init ima_init_policy(void)
                          ARRAY_SIZE(default_appraise_rules),
                          IMA_DEFAULT_POLICY);
 
+       if (ima_use_critical_data)
+               add_rules(critical_data_rules,
+                         ARRAY_SIZE(critical_data_rules),
+                         IMA_DEFAULT_POLICY);
+
        ima_update_policy_flag();
 }
 
@@ -907,7 +942,7 @@ enum {
        Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
        Opt_appraise_type, Opt_appraise_flag,
        Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
-       Opt_err
+       Opt_label, Opt_err
 };
 
 static const match_table_t policy_tokens = {
@@ -944,6 +979,7 @@ static const match_table_t policy_tokens = {
        {Opt_pcr, "pcr=%s"},
        {Opt_template, "template=%s"},
        {Opt_keyrings, "keyrings=%s"},
+       {Opt_label, "label=%s"},
        {Opt_err, NULL}
 };
 
@@ -1107,6 +1143,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
                        return false;
 
                break;
+       case CRITICAL_DATA:
+               if (entry->action & ~(MEASURE | DONT_MEASURE))
+                       return false;
+
+               if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR |
+                                    IMA_LABEL))
+                       return false;
+
+               if (ima_rule_contains_lsm_cond(entry))
+                       return false;
+
+               break;
        default:
                return false;
        }
@@ -1238,6 +1286,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                        else if (IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) &&
                                 strcmp(args[0].from, "KEY_CHECK") == 0)
                                entry->func = KEY_CHECK;
+                       else if (strcmp(args[0].from, "CRITICAL_DATA") == 0)
+                               entry->func = CRITICAL_DATA;
                        else
                                result = -EINVAL;
                        if (!result)
@@ -1308,6 +1358,23 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 
                        entry->flags |= IMA_KEYRINGS;
                        break;
+               case Opt_label:
+                       ima_log_string(ab, "label", args[0].from);
+
+                       if (entry->label) {
+                               result = -EINVAL;
+                               break;
+                       }
+
+                       entry->label = ima_alloc_rule_opt_list(args);
+                       if (IS_ERR(entry->label)) {
+                               result = PTR_ERR(entry->label);
+                               entry->label = NULL;
+                               break;
+                       }
+
+                       entry->flags |= IMA_LABEL;
+                       break;
                case Opt_fsuuid:
                        ima_log_string(ab, "fsuuid", args[0].from);
 
@@ -1688,6 +1755,12 @@ int ima_policy_show(struct seq_file *m, void *v)
                seq_puts(m, " ");
        }
 
+       if (entry->flags & IMA_LABEL) {
+               seq_puts(m, "label=");
+               ima_show_rule_opt_list(m, entry->label);
+               seq_puts(m, " ");
+       }
+
        if (entry->flags & IMA_PCR) {
                snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
                seq_printf(m, pt(Opt_pcr), tbuf);
index 69a8626..c2f2ad3 100644 (file)
@@ -162,7 +162,8 @@ void ima_process_queued_keys(void)
                                                   entry->payload_len,
                                                   entry->keyring_name,
                                                   KEY_CHECK, 0,
-                                                  entry->keyring_name);
+                                                  entry->keyring_name,
+                                                  false);
                list_del(&entry->list);
                ima_free_key_entry(entry);
        }
index 4d8e0e8..7761624 100644 (file)
@@ -16,6 +16,8 @@ selinux-$(CONFIG_NETLABEL) += netlabel.o
 
 selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o
 
+selinux-$(CONFIG_IMA) += ima.o
+
 ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
 
 $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
diff --git a/security/selinux/ima.c b/security/selinux/ima.c
new file mode 100644 (file)
index 0000000..0371589
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Microsoft Corporation
+ *
+ * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
+ *
+ * Measure critical data structures maintainted by SELinux
+ * using IMA subsystem.
+ */
+#include <linux/vmalloc.h>
+#include <linux/ima.h>
+#include "security.h"
+#include "ima.h"
+
+/*
+ * selinux_ima_measure_state - Measure hash of the SELinux policy
+ *
+ * @state: selinux state struct
+ *
+ * NOTE: This function must be called with policy_mutex held.
+ */
+void selinux_ima_measure_state(struct selinux_state *state)
+{
+       void *policy = NULL;
+       size_t policy_len;
+       int rc = 0;
+
+       /*
+        * Measure SELinux policy only after initialization is completed.
+        */
+       if (!selinux_initialized(state))
+               return;
+
+       rc = security_read_state_kernel(state, &policy, &policy_len);
+       if (rc) {
+               pr_err("SELinux: %s: failed to read policy %d.\n", __func__, rc);
+               return;
+       }
+
+       ima_measure_critical_data("selinux", "selinux-policy-hash",
+                                 policy, policy_len, true);
+
+       vfree(policy);
+}
diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h
new file mode 100644 (file)
index 0000000..d69c366
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2021 Microsoft Corporation
+ *
+ * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
+ *
+ * Measure critical data structures maintainted by SELinux
+ * using IMA subsystem.
+ */
+
+#ifndef _SELINUX_IMA_H_
+#define _SELINUX_IMA_H_
+
+#include "security.h"
+
+#ifdef CONFIG_IMA
+extern void selinux_ima_measure_state(struct selinux_state *selinux_state);
+#else
+static inline void selinux_ima_measure_state(struct selinux_state *selinux_state)
+{
+}
+#endif
+
+#endif /* _SELINUX_IMA_H_ */
index 3cc8bab..29cae32 100644 (file)
@@ -229,7 +229,8 @@ void selinux_policy_cancel(struct selinux_state *state,
                        struct selinux_policy *policy);
 int security_read_policy(struct selinux_state *state,
                         void **data, size_t *len);
-
+int security_read_state_kernel(struct selinux_state *state,
+                              void **data, size_t *len);
 int security_policycap_supported(struct selinux_state *state,
                                 unsigned int req_cap);
 
index 597b797..2106b5d 100644 (file)
@@ -65,6 +65,7 @@
 #include "ebitmap.h"
 #include "audit.h"
 #include "policycap_names.h"
+#include "ima.h"
 
 /* Forward declaration. */
 static int context_struct_to_string(struct policydb *policydb,
@@ -2178,6 +2179,7 @@ static void selinux_notify_policy_change(struct selinux_state *state,
        selinux_status_update_policyload(state, seqno);
        selinux_netlbl_cache_invalidate();
        selinux_xfrm_notify_policyload();
+       selinux_ima_measure_state(state);
 }
 
 void selinux_policy_commit(struct selinux_state *state,
@@ -3874,7 +3876,32 @@ out:
 #endif /* CONFIG_NETLABEL */
 
 /**
+ * __security_read_policy - read the policy.
+ * @policy: SELinux policy
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ */
+static int __security_read_policy(struct selinux_policy *policy,
+                                 void *data, size_t *len)
+{
+       int rc;
+       struct policy_file fp;
+
+       fp.data = data;
+       fp.len = *len;
+
+       rc = policydb_write(&policy->policydb, &fp);
+       if (rc)
+               return rc;
+
+       *len = (unsigned long)fp.data - (unsigned long)data;
+       return 0;
+}
+
+/**
  * security_read_policy - read the policy.
+ * @state: selinux_state
  * @data: binary policy data
  * @len: length of data in bytes
  *
@@ -3883,8 +3910,6 @@ int security_read_policy(struct selinux_state *state,
                         void **data, size_t *len)
 {
        struct selinux_policy *policy;
-       int rc;
-       struct policy_file fp;
 
        policy = rcu_dereference_protected(
                        state->policy, lockdep_is_held(&state->policy_mutex));
@@ -3896,14 +3921,35 @@ int security_read_policy(struct selinux_state *state,
        if (!*data)
                return -ENOMEM;
 
-       fp.data = *data;
-       fp.len = *len;
+       return __security_read_policy(policy, *data, len);
+}
 
-       rc = policydb_write(&policy->policydb, &fp);
-       if (rc)
-               return rc;
+/**
+ * security_read_state_kernel - read the policy.
+ * @state: selinux_state
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ * Allocates kernel memory for reading SELinux policy.
+ * This function is for internal use only and should not
+ * be used for returning data to user space.
+ *
+ * This function must be called with policy_mutex held.
+ */
+int security_read_state_kernel(struct selinux_state *state,
+                              void **data, size_t *len)
+{
+       struct selinux_policy *policy;
 
-       *len = (unsigned long)fp.data - (unsigned long)*data;
-       return 0;
+       policy = rcu_dereference_protected(
+                       state->policy, lockdep_is_held(&state->policy_mutex));
+       if (!policy)
+               return -EINVAL;
+
+       *len = policy->policydb.len;
+       *data = vmalloc(*len);
+       if (!*data)
+               return -ENOMEM;
 
+       return __security_read_policy(policy, *data, len);
 }