Merge tag 'integrity-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
[sfrench/cifs-2.6.git] / security / integrity / ima / ima_appraise.c
index ef9dcfce45d45c45ebddbba47f293ed5903fa28a..dbba51583e7c13143707ebaf194e3e73ef93c04a 100644 (file)
@@ -77,8 +77,9 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
                return 0;
 
        security_task_getsecid_subj(current, &secid);
-       return ima_match_policy(mnt_userns, inode, current_cred(), secid, func,
-                               mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL);
+       return ima_match_policy(mnt_userns, inode, current_cred(), secid,
+                               func, mask, IMA_APPRAISE | IMA_HASH, NULL,
+                               NULL, NULL, NULL);
 }
 
 static int ima_fix_xattr(struct dentry *dentry,
@@ -171,7 +172,7 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
        }
 }
 
-enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
+enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
                                 int xattr_len)
 {
        struct signature_v2_hdr *sig;
@@ -184,7 +185,8 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
        switch (xattr_value->type) {
        case EVM_IMA_XATTR_DIGSIG:
                sig = (typeof(sig))xattr_value;
-               if (sig->version != 2 || xattr_len <= sizeof(*sig))
+               if (sig->version != 2 || xattr_len <= sizeof(*sig)
+                   || sig->hash_algo >= HASH_ALGO__LAST)
                        return ima_hash_algo;
                return sig->hash_algo;
                break;
@@ -357,7 +359,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
                if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
                        process_buffer_measurement(&init_user_ns, NULL, digest, digestsize,
                                                   "blacklisted-hash", NONE,
-                                                  pcr, NULL, false);
+                                                  pcr, NULL, false, NULL, 0);
        }
 
        return rc;
@@ -575,6 +577,66 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
                clear_bit(IMA_DIGSIG, &iint->atomic_flags);
 }
 
+/**
+ * validate_hash_algo() - Block setxattr with unsupported hash algorithms
+ * @dentry: object of the setxattr()
+ * @xattr_value: userland supplied xattr value
+ * @xattr_value_len: length of xattr_value
+ *
+ * The xattr value is mapped to its hash algorithm, and this algorithm
+ * must be built in the kernel for the setxattr to be allowed.
+ *
+ * Emit an audit message when the algorithm is invalid.
+ *
+ * Return: 0 on success, else an error.
+ */
+static int validate_hash_algo(struct dentry *dentry,
+                             const struct evm_ima_xattr_data *xattr_value,
+                             size_t xattr_value_len)
+{
+       char *path = NULL, *pathbuf = NULL;
+       enum hash_algo xattr_hash_algo;
+       const char *errmsg = "unavailable-hash-algorithm";
+       unsigned int allowed_hashes;
+
+       xattr_hash_algo = ima_get_hash_algo(xattr_value, xattr_value_len);
+
+       allowed_hashes = atomic_read(&ima_setxattr_allowed_hash_algorithms);
+
+       if (allowed_hashes) {
+               /* success if the algorithm is allowed in the ima policy */
+               if (allowed_hashes & (1U << xattr_hash_algo))
+                       return 0;
+
+               /*
+                * We use a different audit message when the hash algorithm
+                * is denied by a policy rule, instead of not being built
+                * in the kernel image
+                */
+               errmsg = "denied-hash-algorithm";
+       } else {
+               if (likely(xattr_hash_algo == ima_hash_algo))
+                       return 0;
+
+               /* allow any xattr using an algorithm built in the kernel */
+               if (crypto_has_alg(hash_algo_name[xattr_hash_algo], 0, 0))
+                       return 0;
+       }
+
+       pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+       if (!pathbuf)
+               return -EACCES;
+
+       path = dentry_path(dentry, pathbuf, PATH_MAX);
+
+       integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
+                           "set_data", errmsg, -EACCES, 0);
+
+       kfree(pathbuf);
+
+       return -EACCES;
+}
+
 int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
                       const void *xattr_value, size_t xattr_value_len)
 {
@@ -592,9 +654,11 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
                digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
        }
        if (result == 1 || evm_revalidate_status(xattr_name)) {
+               result = validate_hash_algo(dentry, xvalue, xattr_value_len);
+               if (result)
+                       return result;
+
                ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
-               if (result == 1)
-                       result = 0;
        }
        return result;
 }