Merge tag 'integrity-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
[linux-2.6-microblaze.git] / security / integrity / ima / ima_main.c
index 673833f..4658654 100644 (file)
@@ -35,7 +35,7 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
 int ima_appraise;
 #endif
 
-int ima_hash_algo = HASH_ALGO_SHA1;
+int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1;
 static int hash_setup_done;
 
 static struct notifier_block ima_lsm_policy_notifier = {
@@ -76,6 +76,11 @@ out:
 }
 __setup("ima_hash=", hash_setup);
 
+enum hash_algo ima_get_current_hash_algo(void)
+{
+       return ima_hash_algo;
+}
+
 /* Prevent mmap'ing a file execute that is already mmap'ed write */
 static int mmap_violation_check(enum ima_hooks func, struct file *file,
                                char **pathbuf, const char **pathname,
@@ -210,6 +215,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
        int xattr_len = 0;
        bool violation_check;
        enum hash_algo hash_algo;
+       unsigned int allowed_algos = 0;
 
        if (!ima_policy_flag || !S_ISREG(inode->i_mode))
                return 0;
@@ -219,7 +225,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
         * Included is the appraise submask.
         */
        action = ima_get_action(file_mnt_user_ns(file), inode, cred, secid,
-                               mask, func, &pcr, &template_desc, NULL);
+                               mask, func, &pcr, &template_desc, NULL,
+                               &allowed_algos);
        violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
                           (ima_policy_flag & IMA_MEASURE));
        if (!action && !violation_check)
@@ -356,6 +363,16 @@ static int process_measurement(struct file *file, const struct cred *cred,
 
        if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
                rc = 0;
+
+       /* Ensure the digest was generated using an allowed algorithm */
+       if (rc == 0 && must_appraise && allowed_algos != 0 &&
+           (allowed_algos & (1U << hash_algo)) == 0) {
+               rc = -EACCES;
+
+               integrity_audit_msg(AUDIT_INTEGRITY_DATA, file_inode(file),
+                                   pathname, "collect_data",
+                                   "denied-hash-algorithm", rc, 0);
+       }
 out_locked:
        if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
             !(iint->flags & IMA_NEW_FILE))
@@ -433,7 +450,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
        inode = file_inode(vma->vm_file);
        action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode,
                                current_cred(), secid, MAY_EXEC, MMAP_CHECK,
-                               &pcr, &template, NULL);
+                               &pcr, &template, NULL, NULL);
 
        /* Is the mmap'ed file in policy? */
        if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
@@ -822,7 +839,7 @@ int ima_post_load_data(char *buf, loff_t size,
        return 0;
 }
 
-/*
+/**
  * process_buffer_measurement - Measure the buffer or the buffer data hash
  * @mnt_userns:        user namespace of the mount the inode was found from
  * @inode: inode associated with the object being measured (NULL for KEY_CHECK)
@@ -833,14 +850,20 @@ int ima_post_load_data(char *buf, loff_t size,
  * @pcr: pcr to extend the measurement
  * @func_data: func specific data, may be NULL
  * @buf_hash: measure buffer data hash
+ * @digest: buffer digest will be written to
+ * @digest_len: buffer length
  *
  * Based on policy, either the buffer data or buffer data hash is measured
+ *
+ * Return: 0 if the buffer has been successfully measured, 1 if the digest
+ * has been written to the passed location but not added to a measurement entry,
+ * a negative value otherwise.
  */
-void process_buffer_measurement(struct user_namespace *mnt_userns,
-                               struct inode *inode, const void *buf, int size,
-                               const char *eventname, enum ima_hooks func,
-                               int pcr, const char *func_data,
-                               bool buf_hash)
+int process_buffer_measurement(struct user_namespace *mnt_userns,
+                              struct inode *inode, const void *buf, int size,
+                              const char *eventname, enum ima_hooks func,
+                              int pcr, const char *func_data,
+                              bool buf_hash, u8 *digest, size_t digest_len)
 {
        int ret = 0;
        const char *audit_cause = "ENOMEM";
@@ -861,8 +884,11 @@ void process_buffer_measurement(struct user_namespace *mnt_userns,
        int action = 0;
        u32 secid;
 
-       if (!ima_policy_flag)
-               return;
+       if (digest && digest_len < digest_hash_len)
+               return -EINVAL;
+
+       if (!ima_policy_flag && !digest)
+               return -ENOENT;
 
        template = ima_template_desc_buf();
        if (!template) {
@@ -882,9 +908,9 @@ void process_buffer_measurement(struct user_namespace *mnt_userns,
                security_task_getsecid_subj(current, &secid);
                action = ima_get_action(mnt_userns, inode, current_cred(),
                                        secid, 0, func, &pcr, &template,
-                                       func_data);
-               if (!(action & IMA_MEASURE))
-                       return;
+                                       func_data, NULL);
+               if (!(action & IMA_MEASURE) && !digest)
+                       return -ENOENT;
        }
 
        if (!pcr)
@@ -914,6 +940,12 @@ void process_buffer_measurement(struct user_namespace *mnt_userns,
                event_data.buf_len = digest_hash_len;
        }
 
+       if (digest)
+               memcpy(digest, iint.ima_hash->digest, digest_hash_len);
+
+       if (!ima_policy_flag || (func && !(action & IMA_MEASURE)))
+               return 1;
+
        ret = ima_alloc_init_template(&event_data, &entry, template);
        if (ret < 0) {
                audit_cause = "alloc_entry";
@@ -932,7 +964,7 @@ out:
                                        func_measure_str(func),
                                        audit_cause, ret, 0, ret);
 
-       return;
+       return ret;
 }
 
 /**
@@ -956,7 +988,7 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 
        process_buffer_measurement(file_mnt_user_ns(f.file), file_inode(f.file),
                                   buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0,
-                                  NULL, false);
+                                  NULL, false, NULL, 0);
        fdput(f);
 }
 
@@ -967,23 +999,30 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
  * @buf: pointer to buffer data
  * @buf_len: length of buffer data (in bytes)
  * @hash: measure buffer data hash
+ * @digest: buffer digest will be written to
+ * @digest_len: buffer length
  *
  * 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.
+ *
+ * Return: 0 if the buffer has been successfully measured, 1 if the digest
+ * has been written to the passed location but not added to a measurement entry,
+ * a negative value otherwise.
  */
-void ima_measure_critical_data(const char *event_label,
-                              const char *event_name,
-                              const void *buf, size_t buf_len,
-                              bool hash)
+int ima_measure_critical_data(const char *event_label,
+                             const char *event_name,
+                             const void *buf, size_t buf_len,
+                             bool hash, u8 *digest, size_t digest_len)
 {
        if (!event_name || !event_label || !buf || !buf_len)
-               return;
+               return -ENOPARAM;
 
-       process_buffer_measurement(&init_user_ns, NULL, buf, buf_len, event_name,
-                                  CRITICAL_DATA, 0, event_label,
-                                  hash);
+       return process_buffer_measurement(&init_user_ns, NULL, buf, buf_len,
+                                         event_name, CRITICAL_DATA, 0,
+                                         event_label, hash, digest,
+                                         digest_len);
 }
 EXPORT_SYMBOL_GPL(ima_measure_critical_data);
 
@@ -1013,7 +1052,7 @@ static int __init init_ima(void)
                pr_warn("Couldn't register LSM notifier, error %d\n", error);
 
        if (!error)
-               ima_update_policy_flag();
+               ima_update_policy_flags();
 
        return error;
 }