1 /* Copyright 2022 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include "2common.h"
7 #include "2crypto.h"
8 #include "2return_codes.h"
9 #include "cbfstool.h"
10 #include "host_misc.h"
11 #include "subprocess.h"
12 #include "vboot_host.h"
13
14 #define ARGV_END(region) region ? "-r" : NULL, region, NULL,
15
get_cbfstool_path(void)16 static const char *get_cbfstool_path(void)
17 {
18 static const char *cbfstool = NULL;
19
20 if (cbfstool)
21 return cbfstool;
22
23 const char *env_cbfstool = getenv(ENV_CBFSTOOL);
24 if (env_cbfstool && env_cbfstool[0] != '\0') {
25 cbfstool = strdup(env_cbfstool);
26 return cbfstool;
27 }
28
29 cbfstool = DEFAULT_CBFSTOOL;
30 return cbfstool;
31 }
32
find_cbfs_file(const char * buf,const char * name)33 static bool find_cbfs_file(const char *buf, const char *name)
34 {
35 char *to_find = NULL;
36 if (asprintf(&to_find, "\n%s\t", name) < 0) {
37 fprintf(stderr, "Out of Memory\n");
38 exit(1);
39 }
40
41 const char *start = strstr(buf, to_find);
42
43 free(to_find);
44 return !!start;
45 }
46
cbfstool_file_exists(const char * image_file,const char * region,const char * name)47 bool cbfstool_file_exists(const char *image_file, const char *region,
48 const char *name)
49 {
50 int status;
51 char *buffer;
52 const size_t buffer_size = 1024 * 128;
53 const char *cbfstool = get_cbfstool_path();
54 bool res = false;
55
56 buffer = malloc(buffer_size);
57 if (!buffer)
58 goto done;
59
60 struct subprocess_target output = {
61 .type = TARGET_BUFFER_NULL_TERMINATED,
62 .buffer = {
63 .buf = buffer,
64 .size = buffer_size,
65 },
66 };
67 const char *const argv[] = {
68 cbfstool, image_file, "print", "-k", ARGV_END(region)
69 };
70
71 status = subprocess_run(argv, &subprocess_null, &output,
72 &subprocess_null);
73 if (status < 0) {
74 fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
75 __func__);
76 exit(1);
77 }
78 if (status > 0)
79 goto done;
80
81 res = find_cbfs_file(buffer, name);
82
83 done:
84 free(buffer);
85 return res;
86 }
87
cbfstool_extract(const char * image_file,const char * region,const char * name,const char * file)88 int cbfstool_extract(const char *image_file, const char *region,
89 const char *name, const char *file)
90 {
91 int status;
92 const char *cbfstool = get_cbfstool_path();
93
94 const char *const argv[] = {
95 cbfstool, image_file, "extract", "-n", name, "-f", file,
96 ARGV_END(region)
97 };
98
99 status = subprocess_run(argv, &subprocess_null, &subprocess_null,
100 &subprocess_null);
101 if (status < 0) {
102 fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
103 __func__);
104 exit(1);
105 }
106
107 return status;
108 }
109
cbfstool_truncate(const char * file,const char * region,size_t * new_size)110 vb2_error_t cbfstool_truncate(const char *file, const char *region,
111 size_t *new_size)
112 {
113 int status;
114 char output_buffer[128];
115 const char *cbfstool = get_cbfstool_path();
116
117 struct subprocess_target output = {
118 .type = TARGET_BUFFER_NULL_TERMINATED,
119 .buffer = {
120 .buf = output_buffer,
121 .size = sizeof(output_buffer),
122 },
123 };
124 const char *const argv[] = {
125 cbfstool, file, "truncate", "-r", region, NULL,
126 };
127
128 status = subprocess_run(argv, &subprocess_null, &output,
129 &subprocess_null);
130
131 if (status < 0) {
132 fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
133 __func__);
134 exit(1);
135 }
136
137 /* Positive exit code means something is wrong with image. Return zero
138 as new size, because it might be problem with missing CBFS.*/
139 if (status > 0) {
140 *new_size = 0;
141 return VB2_ERROR_CBFSTOOL;
142 }
143
144 if (sscanf(output_buffer, "%zi", new_size) != 1) {
145 VB2_DEBUG("Failed to parse command output. Unexpected "
146 "output.\n");
147 *new_size = 0;
148 return VB2_ERROR_CBFSTOOL;
149 }
150
151 return VB2_SUCCESS;
152 }
153
154 /* Requires null-terminated buffer */
extract_metadata_hash(const char * buf,struct vb2_hash * hash)155 static vb2_error_t extract_metadata_hash(const char *buf, struct vb2_hash *hash)
156 {
157 enum vb2_hash_algorithm algo;
158 const char *to_find = "\n[METADATA HASH]";
159 char *algo_str = NULL;
160 char *hash_str = NULL;
161 vb2_error_t rv = VB2_ERROR_CBFSTOOL;
162
163 const char *start = strstr(buf, to_find);
164 if (start)
165 start += strlen(to_find);
166
167 if (start) {
168 const int matches = sscanf(start, " %m[^:\n\t ]:%m[^:\n\t ]",
169 &algo_str, &hash_str);
170
171 if (matches < 2)
172 goto done;
173
174 if (!algo_str || !vb2_lookup_hash_alg(algo_str, &algo) ||
175 algo == VB2_HASH_INVALID)
176 goto done;
177 hash->algo = algo;
178
179 if (!hash_str ||
180 strlen(hash_str) != (vb2_digest_size(algo) * 2) ||
181 !parse_hash(&hash->raw[0], vb2_digest_size(algo), hash_str))
182 goto done;
183
184 if (!strstr(buf, "]\tfully valid"))
185 goto done;
186
187 rv = VB2_SUCCESS;
188 }
189
190 done:
191 if (rv != VB2_SUCCESS)
192 hash->algo = VB2_HASH_INVALID;
193
194 free(algo_str);
195 free(hash_str);
196
197 return rv;
198 }
199
cbfstool_get_metadata_hash(const char * file,const char * region,struct vb2_hash * hash)200 vb2_error_t cbfstool_get_metadata_hash(const char *file, const char *region,
201 struct vb2_hash *hash)
202 {
203 int status;
204 const char *cbfstool = get_cbfstool_path();
205 const size_t data_buffer_sz = 1024 * 1024;
206 char *data_buffer = malloc(data_buffer_sz);
207 vb2_error_t rv = VB2_ERROR_CBFSTOOL;
208
209 if (!data_buffer)
210 goto done;
211
212 memset(hash, 0, sizeof(*hash));
213 hash->algo = VB2_HASH_INVALID;
214
215 struct subprocess_target output = {
216 .type = TARGET_BUFFER_NULL_TERMINATED,
217 .buffer = {
218 .buf = data_buffer,
219 .size = data_buffer_sz,
220 },
221 };
222 const char *argv[] = {
223 cbfstool, file, "print", "-kv", ARGV_END(region)
224 };
225
226 status = subprocess_run(argv, &subprocess_null, &output,
227 &subprocess_null);
228
229 if (status < 0) {
230 fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
231 __func__);
232 exit(1);
233 }
234
235 if (status > 0)
236 goto done;
237
238 rv = extract_metadata_hash(data_buffer, hash);
239
240 done:
241 free(data_buffer);
242 return rv;
243 }
244
245 /* Requires null-terminated buffer */
extract_config_value(const char * buf,const char * config_field)246 static char *extract_config_value(const char *buf, const char *config_field)
247 {
248 char *to_find = NULL;
249
250 if (asprintf(&to_find, "\n%s=", config_field) == -1) {
251 fprintf(stderr, "Out of Memory\n");
252 VB2_DIE("Out of memory\n");
253 }
254
255 const char *start = strstr(buf, to_find);
256 if (start)
257 start += strlen(to_find);
258
259 free(to_find);
260
261 if (start) {
262 char *end = strchr(start, '\n');
263 if (end)
264 return strndup(start, end - start);
265 }
266
267 return NULL;
268 }
269
get_config_value(const char * file,const char * region,const char * config_field,char ** value)270 static vb2_error_t get_config_value(const char *file, const char *region,
271 const char *config_field, char **value)
272 {
273 int status;
274 const char *cbfstool = get_cbfstool_path();
275 const size_t data_buffer_sz = 1024 * 1024;
276 char *data_buffer = malloc(data_buffer_sz);
277 vb2_error_t rv = VB2_ERROR_CBFSTOOL;
278
279 *value = NULL;
280
281 if (!data_buffer)
282 goto done;
283
284 struct subprocess_target output = {
285 .type = TARGET_BUFFER_NULL_TERMINATED,
286 .buffer = {
287 .buf = data_buffer,
288 .size = data_buffer_sz,
289 },
290 };
291 const char *argv[] = {
292 cbfstool, file, "extract", "-n", "config", "-f", "/dev/stdout",
293 ARGV_END(region)
294 };
295
296 status = subprocess_run(argv, &subprocess_null, &output,
297 &subprocess_null);
298
299 if (status < 0) {
300 fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
301 __func__);
302 exit(1);
303 }
304
305 if (status > 0)
306 goto done;
307
308 *value = extract_config_value(data_buffer, config_field);
309
310 rv = VB2_SUCCESS;
311 done:
312 free(data_buffer);
313 return rv;
314 }
315
cbfstool_get_config_bool(const char * file,const char * region,const char * config_field,bool * value)316 vb2_error_t cbfstool_get_config_bool(const char *file, const char *region,
317 const char *config_field, bool *value)
318 {
319 vb2_error_t rv;
320 char *raw_value = NULL;
321
322 *value = false;
323
324 rv = get_config_value(file, region, config_field, &raw_value);
325 if (rv)
326 return rv;
327
328 if (raw_value && strcmp(raw_value, "y") == 0)
329 *value = true;
330
331 free(raw_value);
332 return VB2_SUCCESS;
333 }
334
cbfstool_get_config_string(const char * file,const char * region,const char * config_field,char ** value)335 vb2_error_t cbfstool_get_config_string(const char *file, const char *region,
336 const char *config_field, char **value)
337 {
338 vb2_error_t rv;
339 char *raw_value = NULL;
340
341 *value = NULL;
342
343 rv = get_config_value(file, region, config_field, &raw_value);
344 if (rv)
345 return rv;
346
347 /*
348 * With CL:4085654, all the valid configs of type 'str' should be
349 * present in the "config" CBFS file.
350 */
351 if (!raw_value) {
352 VB2_DEBUG("Config '%s' not found in %s.", config_field, file);
353 return VB2_ERROR_CBFSTOOL;
354 }
355
356 size_t len = strlen(raw_value);
357 if (len < 2 || raw_value[0] != '"' || raw_value[len - 1] != '"') {
358 VB2_DEBUG("%s value '%s' is not enclosed with double quotes.",
359 config_field, raw_value);
360 rv = VB2_ERROR_CBFSTOOL;
361 goto done;
362 }
363
364 *value = strndup(&raw_value[1], len - 2);
365
366 done:
367 free(raw_value);
368 return rv;
369 }
370