xref: /aosp_15_r20/external/vboot_reference/host/lib/cbfstool.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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