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 @@
1# SPDX-License-Identifier: GPL-2.01# SPDX-License-Identifier: GPL-2.0
2 2
3obj-$(CONFIG_FS_VERITY) += hash_algs.o \3obj-$(CONFIG_FS_VERITY) += hash_algs.o \
4 init.o4 init.o \
5 open.o
fs/verity/fsverity_private.h+52−2
@@ -15,8 +15,7 @@ · 14 unmodified lines
15#define pr_fmt(fmt) "fs-verity: " fmt15#define pr_fmt(fmt) "fs-verity: " fmt
16 16
17#include <crypto/sha.h>17#include <crypto/sha.h>
18#include <linux/fs.h>18#include <linux/fsverity.h>
19#include <uapi/linux/fsverity.h>
20 19
21struct ahash_request;20struct ahash_request;
22 21
@@ -59,6 +58,40 @@ struct merkle_tree_params { · 36 unmodified lines
59 u64 level_start[FS_VERITY_MAX_LEVELS];58 u64 level_start[FS_VERITY_MAX_LEVELS];
60};59};
61 60
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 */
70struct 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 */
80struct 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
62/* hash_algs.c */95/* hash_algs.c */
63 96
64extern struct fsverity_hash_alg fsverity_hash_algs[];97extern struct fsverity_hash_alg fsverity_hash_algs[];
@@ -85,4 +118,21 @@ fsverity_msg(const struct inode *inode, const char *level, · 20 unmodified lines
85#define fsverity_err(inode, fmt, ...) \118#define fsverity_err(inode, fmt, ...) \
86 fsverity_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)119 fsverity_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)
87 120
121/* open.c */
122
123int 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
129struct fsverity_info *fsverity_create_info(const struct inode *inode,
130 const void *desc, size_t desc_size);
131
132void fsverity_set_info(struct inode *inode, struct fsverity_info *vi);
133
134void fsverity_free_info(struct fsverity_info *vi);
135
136int __init fsverity_init_info_cache(void);
137
88#endif /* _FSVERITY_PRIVATE_H */138#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
33 33
34static int __init fsverity_init(void)34static int __init fsverity_init(void)
35{35{
36 int err;
37
36 fsverity_check_hash_algs();38 fsverity_check_hash_algs();
37 39
40 err = fsverity_init_info_cache();
41 if (err)
42 return err;
43
38 pr_debug("Initialized fs-verity\n");44 pr_debug("Initialized fs-verity\n");
39 return 0;45 return 0;
40}46}
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
12static 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 */
28int 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
119out_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. */
126static 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 */
137struct 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);
201out:
202 if (err) {
203 fsverity_free_info(vi);
204 vi = ERR_PTR(err);
205 }
206 return vi;
207}
208
209void 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
219void 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 */
228static 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;
265out_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 */
283int 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}
296EXPORT_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 */
303void fsverity_cleanup_inode(struct inode *inode)
304{
305 fsverity_free_info(inode->i_verity_info);
306 inode->i_verity_info = NULL;
307}
308EXPORT_SYMBOL_GPL(fsverity_cleanup_inode);
309
310int __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 */
18struct 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
40static 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
48extern int fsverity_file_open(struct inode *inode, struct file *filp);
49extern void fsverity_cleanup_inode(struct inode *inode);
50
51#else /* !CONFIG_FS_VERITY */
52
53static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
54{
55 return NULL;
56}
57
58/* open.c */
59
60static inline int fsverity_file_open(struct inode *inode, struct file *filp)
61{
62 return IS_VERITY(inode) ? -EOPNOTSUPP : 0;
63}
64
65static 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