History

fs-verity: add the hook for file ->open()

Add the fsverity_file_open() function, which prepares an fs-verity file to be read from. If not already done, it loads the fs-verity descriptor from the filesystem and sets up an fsverity_info structure for the inode which describes the Merkle tree and contains the file measurement. It also denies all attempts to open verity files for writing. This commit also begins the include/linux/fsverity.h header, which declares the interface between fs/verity/ and filesystems.

Eric Biggers committed 7 years ago

Commit fd2d1ac1 parent 5585f2a5 files · +449 −3Browse files

Unified · Split

5 files
  1. fs/verity/Makefile +2 −1
  2. fs/verity/fsverity_private.h +52 −2
  3. fs/verity/init.c +6 −0
  4. fs/verity/open.c +318 −0
  5. include/linux/fsverity.h +71 −0
fs/verity/Makefile+2−1
@@ -1,4 +1,5 @@
11 # SPDX-License-Identifier: GPL-2.0
22
33 obj-$(CONFIG_FS_VERITY) += hash_algs.o \
4− init.o
4+ init.o \
5+ open.o
fs/verity/fsverity_private.h+52−2
@@ -15,8 +15,7 @@ · 14 unmodified lines
1515 #define pr_fmt(fmt) "fs-verity: " fmt
1616
1717 #include <crypto/sha.h>
18−#include <linux/fs.h>
19−#include <uapi/linux/fsverity.h>
18+#include <linux/fsverity.h>
2019
2120 struct ahash_request;
2221
@@ -59,6 +58,40 @@ struct merkle_tree_params { · 36 unmodified lines
5958 u64 level_start[FS_VERITY_MAX_LEVELS];
6059 };
6160
61+/**
62+ * fsverity_info - cached verity metadata for an inode
63+ *
64+ * When a verity file is first opened, an instance of this struct is allocated
65+ * and stored in ->i_verity_info; it remains until the inode is evicted. It
66+ * caches information about the Merkle tree that's needed to efficiently verify
67+ * data read from the file. It also caches the file measurement. The Merkle
68+ * tree pages themselves are not cached here, but the filesystem may cache them.
69+ */
70+struct fsverity_info {
71+ struct merkle_tree_params tree_params;
72+ u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE];
73+ u8 measurement[FS_VERITY_MAX_DIGEST_SIZE];
74+ const struct inode *inode;
75+};
76+
77+/*
78+ * Merkle tree properties. The file measurement is the hash of this structure.
79+ */
80+struct fsverity_descriptor {
81+ __u8 version; /* must be 1 */
82+ __u8 hash_algorithm; /* Merkle tree hash algorithm */
83+ __u8 log_blocksize; /* log2 of size of data and tree blocks */
84+ __u8 salt_size; /* size of salt in bytes; 0 if none */
85+ __le32 sig_size; /* reserved, must be 0 */
86+ __le64 data_size; /* size of file the Merkle tree is built over */
87+ __u8 root_hash[64]; /* Merkle tree root hash */
88+ __u8 salt[32]; /* salt prepended to each hashed block */
89+ __u8 __reserved[144]; /* must be 0's */
90+};
91+
92+/* Arbitrary limit to bound the kmalloc() size. Can be changed. */
93+#define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384
94+
6295 /* hash_algs.c */
6396
6497 extern struct fsverity_hash_alg fsverity_hash_algs[];
@@ -85,4 +118,21 @@ fsverity_msg(const struct inode *inode, const char *level, · 20 unmodified lines
85118 #define fsverity_err(inode, fmt, ...) \
86119 fsverity_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)
87120
121+/* open.c */
122+
123+int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
124+ const struct inode *inode,
125+ unsigned int hash_algorithm,
126+ unsigned int log_blocksize,
127+ const u8 *salt, size_t salt_size);
128+
129+struct fsverity_info *fsverity_create_info(const struct inode *inode,
130+ const void *desc, size_t desc_size);
131+
132+void fsverity_set_info(struct inode *inode, struct fsverity_info *vi);
133+
134+void fsverity_free_info(struct fsverity_info *vi);
135+
136+int __init fsverity_init_info_cache(void);
137+
88138 #endif /* _FSVERITY_PRIVATE_H */
fs/verity/init.c+6−0
@@ -33,8 +33,14 @@ void fsverity_msg(const struct inode *inode, const char *level, · 32 unmodified lines
3333
3434 static int __init fsverity_init(void)
3535 {
36+ int err;
37+
3638 fsverity_check_hash_algs();
3739
40+ err = fsverity_init_info_cache();
41+ if (err)
42+ return err;
43+
3844 pr_debug("Initialized fs-verity\n");
3945 return 0;
4046 }
fs/verity/open.c+318−0
@@ -0,0 +1,318 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/*
3+ * fs/verity/open.c: opening fs-verity files
4+ *
5+ * Copyright 2019 Google LLC
6+ */
7+
8+#include "fsverity_private.h"
9+
10+#include <linux/slab.h>
11+
12+static struct kmem_cache *fsverity_info_cachep;
13+
14+/**
15+ * fsverity_init_merkle_tree_params() - initialize Merkle tree parameters
16+ * @params: the parameters struct to initialize
17+ * @inode: the inode for which the Merkle tree is being built
18+ * @hash_algorithm: number of hash algorithm to use
19+ * @log_blocksize: log base 2 of block size to use
20+ * @salt: pointer to salt (optional)
21+ * @salt_size: size of salt, possibly 0
22+ *
23+ * Validate the hash algorithm and block size, then compute the tree topology
24+ * (num levels, num blocks in each level, etc.) and initialize @params.
25+ *
26+ * Return: 0 on success, -errno on failure
27+ */
28+int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
29+ const struct inode *inode,
30+ unsigned int hash_algorithm,
31+ unsigned int log_blocksize,
32+ const u8 *salt, size_t salt_size)
33+{
34+ const struct fsverity_hash_alg *hash_alg;
35+ int err;
36+ u64 blocks;
37+ u64 offset;
38+ int level;
39+
40+ memset(params, 0, sizeof(*params));
41+
42+ hash_alg = fsverity_get_hash_alg(inode, hash_algorithm);
43+ if (IS_ERR(hash_alg))
44+ return PTR_ERR(hash_alg);
45+ params->hash_alg = hash_alg;
46+ params->digest_size = hash_alg->digest_size;
47+
48+ params->hashstate = fsverity_prepare_hash_state(hash_alg, salt,
49+ salt_size);
50+ if (IS_ERR(params->hashstate)) {
51+ err = PTR_ERR(params->hashstate);
52+ params->hashstate = NULL;
53+ fsverity_err(inode, "Error %d preparing hash state", err);
54+ goto out_err;
55+ }
56+
57+ if (log_blocksize != PAGE_SHIFT) {
58+ fsverity_warn(inode, "Unsupported log_blocksize: %u",
59+ log_blocksize);
60+ err = -EINVAL;
61+ goto out_err;
62+ }
63+ params->log_blocksize = log_blocksize;
64+ params->block_size = 1 << log_blocksize;
65+
66+ if (WARN_ON(!is_power_of_2(params->digest_size))) {
67+ err = -EINVAL;
68+ goto out_err;
69+ }
70+ if (params->block_size < 2 * params->digest_size) {
71+ fsverity_warn(inode,
72+ "Merkle tree block size (%u) too small for hash algorithm \"%s\"",
73+ params->block_size, hash_alg->name);
74+ err = -EINVAL;
75+ goto out_err;
76+ }
77+ params->log_arity = params->log_blocksize - ilog2(params->digest_size);
78+ params->hashes_per_block = 1 << params->log_arity;
79+
80+ pr_debug("Merkle tree uses %s with %u-byte blocks (%u hashes/block), salt=%*phN\n",
81+ hash_alg->name, params->block_size, params->hashes_per_block,
82+ (int)salt_size, salt);
83+
84+ /*
85+ * Compute the number of levels in the Merkle tree and create a map from
86+ * level to the starting block of that level. Level 'num_levels - 1' is
87+ * the root and is stored first. Level 0 is the level directly "above"
88+ * the data blocks and is stored last.
89+ */
90+
91+ /* Compute number of levels and the number of blocks in each level */
92+ blocks = (inode->i_size + params->block_size - 1) >> log_blocksize;
93+ pr_debug("Data is %lld bytes (%llu blocks)\n", inode->i_size, blocks);
94+ while (blocks > 1) {
95+ if (params->num_levels >= FS_VERITY_MAX_LEVELS) {
96+ fsverity_err(inode, "Too many levels in Merkle tree");
97+ err = -EINVAL;
98+ goto out_err;
99+ }
100+ blocks = (blocks + params->hashes_per_block - 1) >>
101+ params->log_arity;
102+ /* temporarily using level_start[] to store blocks in level */
103+ params->level_start[params->num_levels++] = blocks;
104+ }
105+
106+ /* Compute the starting block of each level */
107+ offset = 0;
108+ for (level = (int)params->num_levels - 1; level >= 0; level--) {
109+ blocks = params->level_start[level];
110+ params->level_start[level] = offset;
111+ pr_debug("Level %d is %llu blocks starting at index %llu\n",
112+ level, blocks, offset);
113+ offset += blocks;
114+ }
115+
116+ params->tree_size = offset << log_blocksize;
117+ return 0;
118+
119+out_err:
120+ kfree(params->hashstate);
121+ memset(params, 0, sizeof(*params));
122+ return err;
123+}
124+
125+/* Compute the file measurement by hashing the fsverity_descriptor. */
126+static int compute_file_measurement(const struct fsverity_hash_alg *hash_alg,
127+ const struct fsverity_descriptor *desc,
128+ u8 *measurement)
129+{
130+ return fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), measurement);
131+}
132+
133+/*
134+ * Validate the given fsverity_descriptor and create a new fsverity_info from
135+ * it.
136+ */
137+struct fsverity_info *fsverity_create_info(const struct inode *inode,
138+ const void *_desc, size_t desc_size)
139+{
140+ const struct fsverity_descriptor *desc = _desc;
141+ struct fsverity_info *vi;
142+ int err;
143+
144+ if (desc_size < sizeof(*desc)) {
145+ fsverity_err(inode, "Unrecognized descriptor size: %zu bytes",
146+ desc_size);
147+ return ERR_PTR(-EINVAL);
148+ }
149+
150+ if (desc->version != 1) {
151+ fsverity_err(inode, "Unrecognized descriptor version: %u",
152+ desc->version);
153+ return ERR_PTR(-EINVAL);
154+ }
155+
156+ if (desc->sig_size ||
157+ memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) {
158+ fsverity_err(inode, "Reserved bits set in descriptor");
159+ return ERR_PTR(-EINVAL);
160+ }
161+
162+ if (desc->salt_size > sizeof(desc->salt)) {
163+ fsverity_err(inode, "Invalid salt_size: %u", desc->salt_size);
164+ return ERR_PTR(-EINVAL);
165+ }
166+
167+ if (le64_to_cpu(desc->data_size) != inode->i_size) {
168+ fsverity_err(inode,
169+ "Wrong data_size: %llu (desc) != %lld (inode)",
170+ le64_to_cpu(desc->data_size), inode->i_size);
171+ return ERR_PTR(-EINVAL);
172+ }
173+
174+ vi = kmem_cache_zalloc(fsverity_info_cachep, GFP_KERNEL);
175+ if (!vi)
176+ return ERR_PTR(-ENOMEM);
177+ vi->inode = inode;
178+
179+ err = fsverity_init_merkle_tree_params(&vi->tree_params, inode,
180+ desc->hash_algorithm,
181+ desc->log_blocksize,
182+ desc->salt, desc->salt_size);
183+ if (err) {
184+ fsverity_err(inode,
185+ "Error %d initializing Merkle tree parameters",
186+ err);
187+ goto out;
188+ }
189+
190+ memcpy(vi->root_hash, desc->root_hash, vi->tree_params.digest_size);
191+
192+ err = compute_file_measurement(vi->tree_params.hash_alg, desc,
193+ vi->measurement);
194+ if (err) {
195+ fsverity_err(inode, "Error %d computing file measurement", err);
196+ goto out;
197+ }
198+ pr_debug("Computed file measurement: %s:%*phN\n",
199+ vi->tree_params.hash_alg->name,
200+ vi->tree_params.digest_size, vi->measurement);
201+out:
202+ if (err) {
203+ fsverity_free_info(vi);
204+ vi = ERR_PTR(err);
205+ }
206+ return vi;
207+}
208+
209+void fsverity_set_info(struct inode *inode, struct fsverity_info *vi)
210+{
211+ /*
212+ * Multiple processes may race to set ->i_verity_info, so use cmpxchg.
213+ * This pairs with the READ_ONCE() in fsverity_get_info().
214+ */
215+ if (cmpxchg(&inode->i_verity_info, NULL, vi) != NULL)
216+ fsverity_free_info(vi);
217+}
218+
219+void fsverity_free_info(struct fsverity_info *vi)
220+{
221+ if (!vi)
222+ return;
223+ kfree(vi->tree_params.hashstate);
224+ kmem_cache_free(fsverity_info_cachep, vi);
225+}
226+
227+/* Ensure the inode has an ->i_verity_info */
228+static int ensure_verity_info(struct inode *inode)
229+{
230+ struct fsverity_info *vi = fsverity_get_info(inode);
231+ struct fsverity_descriptor *desc;
232+ int res;
233+
234+ if (vi)
235+ return 0;
236+
237+ res = inode->i_sb->s_vop->get_verity_descriptor(inode, NULL, 0);
238+ if (res < 0) {
239+ fsverity_err(inode,
240+ "Error %d getting verity descriptor size", res);
241+ return res;
242+ }
243+ if (res > FS_VERITY_MAX_DESCRIPTOR_SIZE) {
244+ fsverity_err(inode, "Verity descriptor is too large (%d bytes)",
245+ res);
246+ return -EMSGSIZE;
247+ }
248+ desc = kmalloc(res, GFP_KERNEL);
249+ if (!desc)
250+ return -ENOMEM;
251+ res = inode->i_sb->s_vop->get_verity_descriptor(inode, desc, res);
252+ if (res < 0) {
253+ fsverity_err(inode, "Error %d reading verity descriptor", res);
254+ goto out_free_desc;
255+ }
256+
257+ vi = fsverity_create_info(inode, desc, res);
258+ if (IS_ERR(vi)) {
259+ res = PTR_ERR(vi);
260+ goto out_free_desc;
261+ }
262+
263+ fsverity_set_info(inode, vi);
264+ res = 0;
265+out_free_desc:
266+ kfree(desc);
267+ return res;
268+}
269+
270+/**
271+ * fsverity_file_open() - prepare to open a verity file
272+ * @inode: the inode being opened
273+ * @filp: the struct file being set up
274+ *
275+ * When opening a verity file, deny the open if it is for writing. Otherwise,
276+ * set up the inode's ->i_verity_info if not already done.
277+ *
278+ * When combined with fscrypt, this must be called after fscrypt_file_open().
279+ * Otherwise, we won't have the key set up to decrypt the verity metadata.
280+ *
281+ * Return: 0 on success, -errno on failure
282+ */
283+int fsverity_file_open(struct inode *inode, struct file *filp)
284+{
285+ if (!IS_VERITY(inode))
286+ return 0;
287+
288+ if (filp->f_mode & FMODE_WRITE) {
289+ pr_debug("Denying opening verity file (ino %lu) for write\n",
290+ inode->i_ino);
291+ return -EPERM;
292+ }
293+
294+ return ensure_verity_info(inode);
295+}
296+EXPORT_SYMBOL_GPL(fsverity_file_open);
297+
298+/**
299+ * fsverity_cleanup_inode() - free the inode's verity info, if present
300+ *
301+ * Filesystems must call this on inode eviction to free ->i_verity_info.
302+ */
303+void fsverity_cleanup_inode(struct inode *inode)
304+{
305+ fsverity_free_info(inode->i_verity_info);
306+ inode->i_verity_info = NULL;
307+}
308+EXPORT_SYMBOL_GPL(fsverity_cleanup_inode);
309+
310+int __init fsverity_init_info_cache(void)
311+{
312+ fsverity_info_cachep = KMEM_CACHE_USERCOPY(fsverity_info,
313+ SLAB_RECLAIM_ACCOUNT,
314+ measurement);
315+ if (!fsverity_info_cachep)
316+ return -ENOMEM;
317+ return 0;
318+}
include/linux/fsverity.h+71−0
@@ -0,0 +1,71 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * fs-verity: read-only file-based authenticity protection
4+ *
5+ * This header declares the interface between the fs/verity/ support layer and
6+ * filesystems that support fs-verity.
7+ *
8+ * Copyright 2019 Google LLC
9+ */
10+
11+#ifndef _LINUX_FSVERITY_H
12+#define _LINUX_FSVERITY_H
13+
14+#include <linux/fs.h>
15+#include <uapi/linux/fsverity.h>
16+
17+/* Verity operations for filesystems */
18+struct fsverity_operations {
19+
20+ /**
21+ * Get the verity descriptor of the given inode.
22+ *
23+ * @inode: an inode with the S_VERITY flag set
24+ * @buf: buffer in which to place the verity descriptor
25+ * @bufsize: size of @buf, or 0 to retrieve the size only
26+ *
27+ * If bufsize == 0, then the size of the verity descriptor is returned.
28+ * Otherwise the verity descriptor is written to 'buf' and its actual
29+ * size is returned; -ERANGE is returned if it's too large. This may be
30+ * called by multiple processes concurrently on the same inode.
31+ *
32+ * Return: the size on success, -errno on failure
33+ */
34+ int (*get_verity_descriptor)(struct inode *inode, void *buf,
35+ size_t bufsize);
36+};
37+
38+#ifdef CONFIG_FS_VERITY
39+
40+static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
41+{
42+ /* pairs with the cmpxchg() in fsverity_set_info() */
43+ return READ_ONCE(inode->i_verity_info);
44+}
45+
46+/* open.c */
47+
48+extern int fsverity_file_open(struct inode *inode, struct file *filp);
49+extern void fsverity_cleanup_inode(struct inode *inode);
50+
51+#else /* !CONFIG_FS_VERITY */
52+
53+static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
54+{
55+ return NULL;
56+}
57+
58+/* open.c */
59+
60+static inline int fsverity_file_open(struct inode *inode, struct file *filp)
61+{
62+ return IS_VERITY(inode) ? -EOPNOTSUPP : 0;
63+}
64+
65+static inline void fsverity_cleanup_inode(struct inode *inode)
66+{
67+}
68+
69+#endif /* !CONFIG_FS_VERITY */
70+
71+#endif /* _LINUX_FSVERITY_H */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t