1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * fs-verity userspace tool
4 *
5 * Copyright (C) 2018 Google LLC
6 *
7 * Written by Eric Biggers.
8 */
9
10 #include <limits.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #include "commands.h"
16 #include "hash_algs.h"
17
18 static const struct fsverity_command {
19 const char *name;
20 int (*func)(const struct fsverity_command *cmd, int argc, char *argv[]);
21 const char *short_desc;
22 const char *usage_str;
23 } fsverity_commands[] = {
24 {
25 .name = "enable",
26 .func = fsverity_cmd_enable,
27 .short_desc = "Enable fs-verity on a file",
28 .usage_str =
29 " fsverity enable FILE\n"
30 " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
31 " [--signature=SIGFILE]\n"
32 }, {
33 .name = "measure",
34 .func = fsverity_cmd_measure,
35 .short_desc =
36 "Display the measurement of the given verity file(s)",
37 .usage_str =
38 " fsverity measure FILE...\n"
39 }, {
40 .name = "sign",
41 .func = fsverity_cmd_sign,
42 .short_desc = "Sign a file for fs-verity",
43 .usage_str =
44 " fsverity sign FILE OUT_SIGFILE --key=KEYFILE\n"
45 " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
46 " [--cert=CERTFILE]\n"
47 }
48 };
49
usage_all(FILE * fp)50 static void usage_all(FILE *fp)
51 {
52 int i;
53
54 fputs("Usage:\n", fp);
55 for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++)
56 fprintf(fp, " %s:\n%s\n", fsverity_commands[i].short_desc,
57 fsverity_commands[i].usage_str);
58 fputs(
59 " Standard options:\n"
60 " fsverity --help\n"
61 " fsverity --version\n"
62 "\n"
63 "Available hash algorithms: ", fp);
64 show_all_hash_algs(fp);
65 fputs("\nSee `man fsverity` for more details.\n", fp);
66 }
67
usage_cmd(const struct fsverity_command * cmd,FILE * fp)68 static void usage_cmd(const struct fsverity_command *cmd, FILE *fp)
69 {
70 fprintf(fp, "Usage:\n%s", cmd->usage_str);
71 }
72
usage(const struct fsverity_command * cmd,FILE * fp)73 void usage(const struct fsverity_command *cmd, FILE *fp)
74 {
75 if (cmd)
76 usage_cmd(cmd, fp);
77 else
78 usage_all(fp);
79 }
80
81 #define PACKAGE_VERSION "v0.0-alpha"
82 #define PACKAGE_BUGREPORT "linux-fscrypt@vger.kernel.org"
83
show_version(void)84 static void show_version(void)
85 {
86 static const char * const str =
87 "fsverity " PACKAGE_VERSION "\n"
88 "Copyright (C) 2018 Google LLC\n"
89 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
90 "This is free software: you are free to change and redistribute it.\n"
91 "There is NO WARRANTY, to the extent permitted by law.\n"
92 "\n"
93 "Report bugs to " PACKAGE_BUGREPORT ".\n";
94 fputs(str, stdout);
95 }
96
handle_common_options(int argc,char * argv[],const struct fsverity_command * cmd)97 static void handle_common_options(int argc, char *argv[],
98 const struct fsverity_command *cmd)
99 {
100 int i;
101
102 for (i = 1; i < argc; i++) {
103 const char *arg = argv[i];
104
105 if (*arg++ != '-')
106 continue;
107 if (*arg++ != '-')
108 continue;
109 if (!strcmp(arg, "help")) {
110 usage(cmd, stdout);
111 exit(0);
112 } else if (!strcmp(arg, "version")) {
113 show_version();
114 exit(0);
115 } else if (!*arg) /* reached "--", no more options */
116 return;
117 }
118 }
119
find_command(const char * name)120 static const struct fsverity_command *find_command(const char *name)
121 {
122 int i;
123
124 for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++)
125 if (!strcmp(name, fsverity_commands[i].name))
126 return &fsverity_commands[i];
127 return NULL;
128 }
129
parse_block_size_option(const char * arg,u32 * size_ptr)130 bool parse_block_size_option(const char *arg, u32 *size_ptr)
131 {
132 char *end;
133 unsigned long n = strtoul(arg, &end, 10);
134
135 if (*size_ptr != 0) {
136 error_msg("--block-size can only be specified once");
137 return false;
138 }
139
140 if (n <= 0 || n >= INT_MAX || !is_power_of_2(n) || *end != '\0') {
141 error_msg("Invalid block size: %s. Must be power of 2", arg);
142 return false;
143 }
144 *size_ptr = n;
145 return true;
146 }
147
parse_salt_option(const char * arg,u8 ** salt_ptr,u32 * salt_size_ptr)148 bool parse_salt_option(const char *arg, u8 **salt_ptr, u32 *salt_size_ptr)
149 {
150 if (*salt_ptr != NULL) {
151 error_msg("--salt can only be specified once");
152 return false;
153 }
154 *salt_size_ptr = strlen(arg) / 2;
155 *salt_ptr = xmalloc(*salt_size_ptr);
156 if (!hex2bin(arg, *salt_ptr, *salt_size_ptr)) {
157 error_msg("salt is not a valid hex string");
158 return false;
159 }
160 return true;
161 }
162
get_default_block_size(void)163 u32 get_default_block_size(void)
164 {
165 long n = sysconf(_SC_PAGESIZE);
166
167 if (n <= 0 || n >= INT_MAX || !is_power_of_2(n)) {
168 fprintf(stderr,
169 "Warning: invalid _SC_PAGESIZE (%ld). Assuming 4K blocks.\n",
170 n);
171 return 4096;
172 }
173 return n;
174 }
175
main(int argc,char * argv[])176 int main(int argc, char *argv[])
177 {
178 const struct fsverity_command *cmd;
179
180 if (argc < 2) {
181 error_msg("no command specified");
182 usage_all(stderr);
183 return 2;
184 }
185
186 cmd = find_command(argv[1]);
187
188 handle_common_options(argc, argv, cmd);
189
190 if (!cmd) {
191 error_msg("unrecognized command: '%s'", argv[1]);
192 usage_all(stderr);
193 return 2;
194 }
195 return cmd->func(cmd, argc - 1, argv + 1);
196 }
197