1 // SPDX-License-Identifier: MIT
2 /*
3 * fs-verity userspace tool
4 *
5 * Copyright 2018 Google LLC
6 *
7 * Use of this source code is governed by an MIT-style
8 * license that can be found in the LICENSE file or at
9 * https://opensource.org/licenses/MIT.
10 */
11
12 #include "fsverity.h"
13
14 #include <fcntl.h>
15 #include <limits.h>
16
17 static const struct fsverity_command {
18 const char *name;
19 int (*func)(const struct fsverity_command *cmd, int argc, char *argv[]);
20 const char *short_desc;
21 const char *usage_str;
22 } fsverity_commands[] = {
23 {
24 .name = "digest",
25 .func = fsverity_cmd_digest,
26 .short_desc =
27 "Compute the fs-verity digest of the given file(s), for offline signing",
28 .usage_str =
29 " fsverity digest FILE...\n"
30 " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
31 " [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n"
32 " [--compact] [--for-builtin-sig]\n"
33 #ifndef _WIN32
34 }, {
35 .name = "dump_metadata",
36 .func = fsverity_cmd_dump_metadata,
37 .short_desc = "Dump the fs-verity metadata of the given file",
38 .usage_str =
39 " fsverity dump_metadata TYPE FILE [--offset=OFFSET] [--length=LENGTH]\n"
40 }, {
41 .name = "enable",
42 .func = fsverity_cmd_enable,
43 .short_desc = "Enable fs-verity on a file",
44 .usage_str =
45 " fsverity enable FILE\n"
46 " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
47 " [--signature=SIGFILE]\n"
48 }, {
49 .name = "measure",
50 .func = fsverity_cmd_measure,
51 .short_desc =
52 "Display the fs-verity digest of the given verity file(s)",
53 .usage_str =
54 " fsverity measure FILE...\n"
55 #endif /* !_WIN32 */
56 }, {
57 .name = "sign",
58 .func = fsverity_cmd_sign,
59 .short_desc = "Sign a file for fs-verity built-in signature verification",
60 .usage_str =
61 " fsverity sign FILE OUT_SIGFILE\n"
62 " [--key=KEYFILE] [--cert=CERTFILE] [--pkcs11-engine=SOFILE]\n"
63 " [--pkcs11-module=SOFILE] [--pkcs11-keyid=KEYID]\n"
64 " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
65 " [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n"
66 }
67 };
68
show_all_hash_algs(FILE * fp)69 static void show_all_hash_algs(FILE *fp)
70 {
71 u32 alg_num = 1;
72 const char *name;
73
74 fprintf(fp, "Available hash algorithms:");
75 while ((name = libfsverity_get_hash_name(alg_num++)) != NULL)
76 fprintf(fp, " %s", name);
77 putc('\n', fp);
78 }
79
usage_all(FILE * fp)80 static void usage_all(FILE *fp)
81 {
82 int i;
83
84 fputs("Usage:\n", fp);
85 for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++)
86 fprintf(fp, " %s:\n%s\n", fsverity_commands[i].short_desc,
87 fsverity_commands[i].usage_str);
88 fputs(
89 " Standard options:\n"
90 " fsverity --help\n"
91 " fsverity --version\n"
92 "\n", fp);
93 show_all_hash_algs(fp);
94 }
95
usage_cmd(const struct fsverity_command * cmd,FILE * fp)96 static void usage_cmd(const struct fsverity_command *cmd, FILE *fp)
97 {
98 fprintf(fp, "Usage:\n%s", cmd->usage_str);
99 }
100
usage(const struct fsverity_command * cmd,FILE * fp)101 void usage(const struct fsverity_command *cmd, FILE *fp)
102 {
103 if (cmd)
104 usage_cmd(cmd, fp);
105 else
106 usage_all(fp);
107 }
108
show_version(void)109 static void show_version(void)
110 {
111 printf("fsverity v%d.%d\n", FSVERITY_UTILS_MAJOR_VERSION,
112 FSVERITY_UTILS_MINOR_VERSION);
113 }
114
handle_common_options(int argc,char * argv[],const struct fsverity_command * cmd)115 static void handle_common_options(int argc, char *argv[],
116 const struct fsverity_command *cmd)
117 {
118 int i;
119
120 for (i = 1; i < argc; i++) {
121 const char *arg = argv[i];
122
123 if (*arg++ != '-')
124 continue;
125 if (*arg++ != '-')
126 continue;
127 if (!strcmp(arg, "help")) {
128 usage(cmd, stdout);
129 exit(0);
130 } else if (!strcmp(arg, "version")) {
131 show_version();
132 exit(0);
133 } else if (!*arg) /* reached "--", no more options */
134 return;
135 }
136 }
137
find_command(const char * name)138 static const struct fsverity_command *find_command(const char *name)
139 {
140 int i;
141
142 for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++)
143 if (!strcmp(name, fsverity_commands[i].name))
144 return &fsverity_commands[i];
145 return NULL;
146 }
147
parse_hash_alg_option(const char * arg,u32 * alg_ptr)148 static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
149 {
150 char *end;
151 unsigned long n = strtoul(arg, &end, 10);
152
153 if (*alg_ptr != 0) {
154 error_msg("--hash-alg can only be specified once");
155 return false;
156 }
157
158 /* Specified by number? */
159 if (n > 0 && n < INT32_MAX && *end == '\0') {
160 *alg_ptr = n;
161 return true;
162 }
163
164 /* Specified by name? */
165 *alg_ptr = libfsverity_find_hash_alg_by_name(arg);
166 if (*alg_ptr)
167 return true;
168 error_msg("unknown hash algorithm: '%s'", arg);
169 show_all_hash_algs(stderr);
170 return false;
171 }
172
parse_block_size_option(const char * arg,u32 * size_ptr)173 static bool parse_block_size_option(const char *arg, u32 *size_ptr)
174 {
175 char *end;
176 unsigned long n = strtoul(arg, &end, 10);
177
178 if (*size_ptr != 0) {
179 error_msg("--block-size can only be specified once");
180 return false;
181 }
182
183 if (n <= 0 || n >= INT_MAX || !is_power_of_2(n) || *end != '\0') {
184 error_msg("Invalid block size: %s. Must be power of 2", arg);
185 return false;
186 }
187 *size_ptr = n;
188 return true;
189 }
190
parse_salt_option(const char * arg,u8 ** salt_ptr,u32 * salt_size_ptr)191 static bool parse_salt_option(const char *arg, u8 **salt_ptr,
192 u32 *salt_size_ptr)
193 {
194 if (*salt_ptr != NULL) {
195 error_msg("--salt can only be specified once");
196 return false;
197 }
198 *salt_size_ptr = strlen(arg) / 2;
199 *salt_ptr = xmalloc(*salt_size_ptr);
200 if (!hex2bin(arg, *salt_ptr, *salt_size_ptr)) {
201 error_msg("salt is not a valid hex string");
202 return false;
203 }
204 return true;
205 }
206
207 struct metadata_callback_ctx {
208 struct filedes merkle_tree_file;
209 struct filedes descriptor_file;
210 struct libfsverity_metadata_callbacks callbacks;
211 };
212
handle_merkle_tree_size(void * _ctx,u64 size)213 static int handle_merkle_tree_size(void *_ctx, u64 size)
214 {
215 struct metadata_callback_ctx *ctx = _ctx;
216
217 if (!preallocate_file(&ctx->merkle_tree_file, size))
218 return -EIO;
219 return 0;
220 }
221
handle_merkle_tree_block(void * _ctx,const void * block,size_t size,u64 offset)222 static int handle_merkle_tree_block(void *_ctx, const void *block, size_t size,
223 u64 offset)
224 {
225 struct metadata_callback_ctx *ctx = _ctx;
226
227 if (!full_pwrite(&ctx->merkle_tree_file, block, size, offset))
228 return -EIO;
229 return 0;
230 }
231
handle_descriptor(void * _ctx,const void * descriptor,size_t size)232 static int handle_descriptor(void *_ctx, const void *descriptor, size_t size)
233 {
234 struct metadata_callback_ctx *ctx = _ctx;
235
236 if (!full_write(&ctx->descriptor_file, descriptor, size))
237 return -EIO;
238 return 0;
239 }
240
parse_out_metadata_option(int opt_char,const char * arg,const struct libfsverity_metadata_callbacks ** cbs)241 static bool parse_out_metadata_option(int opt_char, const char *arg,
242 const struct libfsverity_metadata_callbacks **cbs)
243 {
244 struct metadata_callback_ctx *ctx;
245 struct filedes *file;
246 const char *opt_name;
247
248 if (*cbs) {
249 ctx = (*cbs)->ctx;
250 } else {
251 ctx = xzalloc(sizeof(*ctx));
252 ctx->merkle_tree_file.fd = -1;
253 ctx->descriptor_file.fd = -1;
254 ctx->callbacks.ctx = ctx;
255 *cbs = &ctx->callbacks;
256 }
257
258 if (opt_char == OPT_OUT_MERKLE_TREE) {
259 file = &ctx->merkle_tree_file;
260 opt_name = "--out-merkle-tree";
261 ctx->callbacks.merkle_tree_size = handle_merkle_tree_size;
262 ctx->callbacks.merkle_tree_block = handle_merkle_tree_block;
263 } else {
264 file = &ctx->descriptor_file;
265 opt_name = "--out-descriptor";
266 ctx->callbacks.descriptor = handle_descriptor;
267 }
268 if (file->fd >= 0) {
269 error_msg("%s can only be specified once", opt_name);
270 return false;
271 }
272 return open_file(file, arg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
273 }
274
parse_tree_param(int opt_char,const char * arg,struct libfsverity_merkle_tree_params * params)275 bool parse_tree_param(int opt_char, const char *arg,
276 struct libfsverity_merkle_tree_params *params)
277 {
278 switch (opt_char) {
279 case OPT_HASH_ALG:
280 return parse_hash_alg_option(arg, ¶ms->hash_algorithm);
281 case OPT_BLOCK_SIZE:
282 return parse_block_size_option(arg, ¶ms->block_size);
283 case OPT_SALT:
284 return parse_salt_option(arg, (u8 **)¶ms->salt,
285 ¶ms->salt_size);
286 case OPT_OUT_MERKLE_TREE:
287 case OPT_OUT_DESCRIPTOR:
288 return parse_out_metadata_option(opt_char, arg,
289 ¶ms->metadata_callbacks);
290 default:
291 ASSERT(0);
292 }
293 }
294
destroy_tree_params(struct libfsverity_merkle_tree_params * params)295 bool destroy_tree_params(struct libfsverity_merkle_tree_params *params)
296 {
297 bool ok = true;
298
299 free((u8 *)params->salt);
300 if (params->metadata_callbacks) {
301 struct metadata_callback_ctx *ctx =
302 params->metadata_callbacks->ctx;
303
304 ok &= filedes_close(&ctx->merkle_tree_file);
305 ok &= filedes_close(&ctx->descriptor_file);
306 free(ctx);
307 }
308 memset(params, 0, sizeof(*params));
309 return ok;
310 }
311
main(int argc,char * argv[])312 int main(int argc, char *argv[])
313 {
314 const struct fsverity_command *cmd;
315
316 install_libfsverity_error_handler();
317
318 if (argc < 2) {
319 error_msg("no command specified");
320 usage_all(stderr);
321 return 2;
322 }
323
324 cmd = find_command(argv[1]);
325
326 handle_common_options(argc, argv, cmd);
327
328 if (!cmd) {
329 error_msg("unrecognized command: '%s'", argv[1]);
330 usage_all(stderr);
331 return 2;
332 }
333 return cmd->func(cmd, argc - 1, argv + 1);
334 }
335