162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ioctl to get a verity file's digest
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2019 Google LLC
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "fsverity_private.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/uaccess.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/**
1362306a36Sopenharmony_ci * fsverity_ioctl_measure() - get a verity file's digest
1462306a36Sopenharmony_ci * @filp: file to get digest of
1562306a36Sopenharmony_ci * @_uarg: user pointer to fsverity_digest
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Retrieve the file digest that the kernel is enforcing for reads from a verity
1862306a36Sopenharmony_ci * file.  See the "FS_IOC_MEASURE_VERITY" section of
1962306a36Sopenharmony_ci * Documentation/filesystems/fsverity.rst for the documentation.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Return: 0 on success, -errno on failure
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ciint fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	const struct inode *inode = file_inode(filp);
2662306a36Sopenharmony_ci	struct fsverity_digest __user *uarg = _uarg;
2762306a36Sopenharmony_ci	const struct fsverity_info *vi;
2862306a36Sopenharmony_ci	const struct fsverity_hash_alg *hash_alg;
2962306a36Sopenharmony_ci	struct fsverity_digest arg;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	vi = fsverity_get_info(inode);
3262306a36Sopenharmony_ci	if (!vi)
3362306a36Sopenharmony_ci		return -ENODATA; /* not a verity file */
3462306a36Sopenharmony_ci	hash_alg = vi->tree_params.hash_alg;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/*
3762306a36Sopenharmony_ci	 * The user specifies the digest_size their buffer has space for; we can
3862306a36Sopenharmony_ci	 * return the digest if it fits in the available space.  We write back
3962306a36Sopenharmony_ci	 * the actual size, which may be shorter than the user-specified size.
4062306a36Sopenharmony_ci	 */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (get_user(arg.digest_size, &uarg->digest_size))
4362306a36Sopenharmony_ci		return -EFAULT;
4462306a36Sopenharmony_ci	if (arg.digest_size < hash_alg->digest_size)
4562306a36Sopenharmony_ci		return -EOVERFLOW;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	memset(&arg, 0, sizeof(arg));
4862306a36Sopenharmony_ci	arg.digest_algorithm = hash_alg - fsverity_hash_algs;
4962306a36Sopenharmony_ci	arg.digest_size = hash_alg->digest_size;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (copy_to_user(uarg, &arg, sizeof(arg)))
5262306a36Sopenharmony_ci		return -EFAULT;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (copy_to_user(uarg->digest, vi->file_digest, hash_alg->digest_size))
5562306a36Sopenharmony_ci		return -EFAULT;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return 0;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/**
6262306a36Sopenharmony_ci * fsverity_get_digest() - get a verity file's digest
6362306a36Sopenharmony_ci * @inode: inode to get digest of
6462306a36Sopenharmony_ci * @raw_digest: (out) the raw file digest
6562306a36Sopenharmony_ci * @alg: (out) the digest's algorithm, as a FS_VERITY_HASH_ALG_* value
6662306a36Sopenharmony_ci * @halg: (out) the digest's algorithm, as a HASH_ALGO_* value
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * Retrieves the fsverity digest of the given file.  The file must have been
6962306a36Sopenharmony_ci * opened at least once since the inode was last loaded into the inode cache;
7062306a36Sopenharmony_ci * otherwise this function will not recognize when fsverity is enabled.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * The file's fsverity digest consists of @raw_digest in combination with either
7362306a36Sopenharmony_ci * @alg or @halg.  (The caller can choose which one of @alg or @halg to use.)
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * IMPORTANT: Callers *must* make use of one of the two algorithm IDs, since
7662306a36Sopenharmony_ci * @raw_digest is meaningless without knowing which algorithm it uses!  fsverity
7762306a36Sopenharmony_ci * provides no security guarantee for users who ignore the algorithm ID, even if
7862306a36Sopenharmony_ci * they use the digest size (since algorithms can share the same digest size).
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Return: The size of the raw digest in bytes, or 0 if the file doesn't have
8162306a36Sopenharmony_ci *	   fsverity enabled.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_ciint fsverity_get_digest(struct inode *inode,
8462306a36Sopenharmony_ci			u8 raw_digest[FS_VERITY_MAX_DIGEST_SIZE],
8562306a36Sopenharmony_ci			u8 *alg, enum hash_algo *halg)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	const struct fsverity_info *vi;
8862306a36Sopenharmony_ci	const struct fsverity_hash_alg *hash_alg;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	vi = fsverity_get_info(inode);
9162306a36Sopenharmony_ci	if (!vi)
9262306a36Sopenharmony_ci		return 0; /* not a verity file */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	hash_alg = vi->tree_params.hash_alg;
9562306a36Sopenharmony_ci	memcpy(raw_digest, vi->file_digest, hash_alg->digest_size);
9662306a36Sopenharmony_ci	if (alg)
9762306a36Sopenharmony_ci		*alg = hash_alg - fsverity_hash_algs;
9862306a36Sopenharmony_ci	if (halg)
9962306a36Sopenharmony_ci		*halg = hash_alg->algo_id;
10062306a36Sopenharmony_ci	return hash_alg->digest_size;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsverity_get_digest);
103