xref: /aosp_15_r20/external/vboot_reference/futility/misc.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2014 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 <assert.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #if !defined(HAVE_MACOS) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
10 #include <linux/fs.h>		/* For BLKGETSIZE64 */
11 #include <sys/sendfile.h>
12 #else
13 #include <copyfile.h>
14 #endif
15 #include <stdarg.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/ioctl.h>
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 
27 #include "2common.h"
28 #include "2sha.h"
29 #include "2sysincludes.h"
30 #include "cgptlib_internal.h"
31 #include "file_type.h"
32 #include "futility.h"
33 #include "futility_options.h"
34 #include "host_misc.h"
35 
36 /* Default is to support everything we can */
37 enum vboot_version vboot_version = VBOOT_VERSION_ALL;
38 
39 int debugging_enabled;
vb2ex_printf(const char * func,const char * format,...)40 void vb2ex_printf(const char *func, const char *format, ...)
41 {
42 	if (!debugging_enabled)
43 		return;
44 
45 	va_list ap;
46 	va_start(ap, format);
47 	if (func)
48 		fprintf(stderr, "DEBUG: %s: ", func);
49 	vfprintf(stderr, format, ap);
50 	va_end(ap);
51 }
52 
is_null_terminated(const char * s,int len)53 static int is_null_terminated(const char *s, int len)
54 {
55 	len--;
56 	s += len;
57 	while (len-- >= 0)
58 		if (!*s--)
59 			return 1;
60 	return 0;
61 }
62 
max(uint32_t a,uint32_t b)63 static inline uint32_t max(uint32_t a, uint32_t b)
64 {
65 	return a > b ? a : b;
66 }
67 
ft_recognize_gbb(uint8_t * buf,uint32_t len)68 enum futil_file_type ft_recognize_gbb(uint8_t *buf, uint32_t len)
69 {
70 	struct vb2_gbb_header *gbb = (struct vb2_gbb_header *)buf;
71 
72 	if (memcmp(gbb->signature, VB2_GBB_SIGNATURE, VB2_GBB_SIGNATURE_SIZE))
73 		return FILE_TYPE_UNKNOWN;
74 	if (gbb->major_version > VB2_GBB_MAJOR_VER)
75 		return FILE_TYPE_UNKNOWN;
76 	if (sizeof(struct vb2_gbb_header) > len)
77 		return FILE_TYPE_UNKNOWN;
78 
79 	/* close enough */
80 	return FILE_TYPE_GBB;
81 }
82 
futil_valid_gbb_header(struct vb2_gbb_header * gbb,uint32_t len,uint32_t * maxlen_ptr)83 int futil_valid_gbb_header(struct vb2_gbb_header *gbb, uint32_t len,
84 			   uint32_t *maxlen_ptr)
85 {
86 	if (len < sizeof(struct vb2_gbb_header))
87 		return 0;
88 
89 	if (memcmp(gbb->signature, VB2_GBB_SIGNATURE, VB2_GBB_SIGNATURE_SIZE))
90 		return 0;
91 	if (gbb->major_version != VB2_GBB_MAJOR_VER)
92 		return 0;
93 
94 	/* Check limits first, to help identify problems */
95 	if (maxlen_ptr) {
96 		uint32_t maxlen = gbb->header_size;
97 		maxlen = max(maxlen,
98 			     gbb->hwid_offset + gbb->hwid_size);
99 		maxlen = max(maxlen,
100 			     gbb->rootkey_offset + gbb->rootkey_size);
101 		maxlen = max(maxlen,
102 			     gbb->bmpfv_offset + gbb->bmpfv_size);
103 		maxlen = max(maxlen,
104 			     gbb->recovery_key_offset + gbb->recovery_key_size);
105 		*maxlen_ptr = maxlen;
106 	}
107 
108 	if (gbb->header_size != EXPECTED_VB2_GBB_HEADER_SIZE ||
109 	    gbb->header_size > len)
110 		return 0;
111 	if (gbb->hwid_offset < EXPECTED_VB2_GBB_HEADER_SIZE)
112 		return 0;
113 	if ((uint64_t)gbb->hwid_offset + gbb->hwid_size > len)
114 		return 0;
115 	if (gbb->hwid_size) {
116 		const char *s = (const char *)
117 			((uint8_t *)gbb + gbb->hwid_offset);
118 		if (!is_null_terminated(s, gbb->hwid_size))
119 			return 0;
120 	}
121 	if (gbb->rootkey_offset < EXPECTED_VB2_GBB_HEADER_SIZE)
122 		return 0;
123 	if ((uint64_t)gbb->rootkey_offset + gbb->rootkey_size > len)
124 		return 0;
125 
126 	if (gbb->bmpfv_offset < EXPECTED_VB2_GBB_HEADER_SIZE)
127 		return 0;
128 	if ((uint64_t)gbb->bmpfv_offset + gbb->bmpfv_size > len)
129 		return 0;
130 	if (gbb->recovery_key_offset < EXPECTED_VB2_GBB_HEADER_SIZE)
131 		return 0;
132 	if ((uint64_t)gbb->recovery_key_offset + gbb->recovery_key_size > len)
133 		return 0;
134 
135 	/* Seems legit... */
136 	return 1;
137 }
138 
139 /* For GBB v1.2 and later, print the stored digest of the HWID (and whether
140  * it's correct). Return true if it is correct. */
print_hwid_digest(struct vb2_gbb_header * gbb,const char * banner)141 int print_hwid_digest(struct vb2_gbb_header *gbb, const char *banner)
142 {
143 	FT_READABLE_PRINT("%s", banner);
144 	FT_PARSEABLE_PRINT("hwid::digest::algorithm::2::SHA256\n");
145 	FT_PARSEABLE_PRINT("hwid::digest::hex::");
146 
147 	/* There isn't one for v1.1 and earlier, so assume it's good. */
148 	if (gbb->minor_version < 2) {
149 		printf("<none>\n");
150 		FT_PARSEABLE_PRINT("hwid::digest::ignored\n");
151 		return 1;
152 	}
153 
154 	uint8_t *buf = (uint8_t *)gbb;
155 	char *hwid_str = (char *)(buf + gbb->hwid_offset);
156 	int is_valid = 0;
157 	struct vb2_hash hash;
158 
159 	if (VB2_SUCCESS == vb2_hash_calculate(false, buf + gbb->hwid_offset,
160 					      strlen(hwid_str), VB2_HASH_SHA256,
161 					      &hash)) {
162 		int i;
163 		is_valid = 1;
164 		/* print it, comparing as we go */
165 		for (i = 0; i < sizeof(hash.sha256); i++) {
166 			printf("%02x", gbb->hwid_digest[i]);
167 			if (gbb->hwid_digest[i] != hash.sha256[i])
168 				is_valid = 0;
169 		}
170 	}
171 
172 	FT_PRINT_RAW("", "\n");
173 	FT_PRINT("   %s\n", "hwid::digest::%s\n", is_valid ? "valid" : "invalid");
174 	return is_valid;
175 }
176 
177 /* Deprecated. Use futil_set_gbb_hwid in future. */
178 /* For GBB v1.2 and later, update the hwid_digest field. */
update_hwid_digest(struct vb2_gbb_header * gbb)179 void update_hwid_digest(struct vb2_gbb_header *gbb)
180 {
181 	/* There isn't one for v1.1 and earlier */
182 	if (gbb->minor_version < 2)
183 		return;
184 
185 	uint8_t *buf = (uint8_t *)gbb;
186 	char *hwid_str = (char *)(buf + gbb->hwid_offset);
187 	struct vb2_hash hash;
188 
189 	vb2_hash_calculate(false, buf + gbb->hwid_offset, strlen(hwid_str),
190 			   VB2_HASH_SHA256, &hash);
191 	memcpy(gbb->hwid_digest, hash.raw, sizeof(gbb->hwid_digest));
192 }
193 
194 /* Sets the HWID string field inside a GBB header. */
futil_set_gbb_hwid(struct vb2_gbb_header * gbb,const char * hwid)195 int futil_set_gbb_hwid(struct vb2_gbb_header *gbb, const char *hwid)
196 {
197 	uint8_t *to = (uint8_t *)gbb + gbb->hwid_offset;
198 	struct vb2_hash hash;
199 	size_t len;
200 
201 	assert(hwid);
202 	len = strlen(hwid);
203 	if (len >= gbb->hwid_size)
204 		return -1;
205 
206 	/* Zero whole area so we won't have garbage after NUL. */
207 	memset(to, 0, gbb->hwid_size);
208 	memcpy(to, hwid, len);
209 
210 	/* major_version starts from 1 and digest must be updated since v1.2. */
211 	if (gbb->major_version == 1 && gbb->minor_version < 2)
212 		return 0;
213 
214 	VB2_TRY(vb2_hash_calculate(false, to, len, VB2_HASH_SHA256, &hash));
215 	memcpy(gbb->hwid_digest, hash.raw, sizeof(gbb->hwid_digest));
216 	return VB2_SUCCESS;
217 }
218 
futil_copy_file(const char * infile,const char * outfile)219 int futil_copy_file(const char *infile, const char *outfile)
220 {
221 	VB2_DEBUG("%s -> %s\n", infile, outfile);
222 
223 	int ifd, ofd;
224 	if ((ifd = open(infile, O_RDONLY)) == -1) {
225 		ERROR("Cannot open '%s', %s.\n", infile, strerror(errno));
226 		return -1;
227 	}
228 	if ((ofd = creat(outfile, 0660)) == -1) {
229 		ERROR("Cannot open '%s', %s.\n", outfile, strerror(errno));
230 		close(ifd);
231 		return -1;
232 	}
233 	struct stat finfo = {0};
234 	if (fstat(ifd, &finfo) < 0) {
235 		ERROR("Cannot fstat '%s' as %s.\n", infile, strerror(errno));
236 		close (ifd);
237 		close (ofd);
238 		return -1;
239 	}
240 #if !defined(HAVE_MACOS) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
241 	ssize_t ret = sendfile(ofd, ifd, NULL, finfo.st_size);
242 #else
243 	ssize_t ret = fcopyfile(ifd, ofd, 0, COPYFILE_ALL);
244 #endif
245 	close(ifd);
246 	close(ofd);
247 	if (ret == -1) {
248 		ERROR("Cannot copy '%s'->'%s', %s.\n", infile,
249 		      outfile, strerror(errno));
250 	}
251 	return ret;
252 }
253 
futil_open_file(const char * infile,int * fd,enum file_mode mode)254 enum futil_file_err futil_open_file(const char *infile, int *fd,
255 				    enum file_mode mode)
256 {
257 	if (mode == FILE_RW) {
258 		VB2_DEBUG("open RW %s\n", infile);
259 		*fd = open(infile, O_RDWR);
260 		if (*fd < 0) {
261 			ERROR("Can't open %s for writing: %s\n", infile,
262 			      strerror(errno));
263 			return FILE_ERR_OPEN;
264 		}
265 	} else {
266 		VB2_DEBUG("open RO %s\n", infile);
267 		*fd = open(infile, O_RDONLY);
268 		if (*fd < 0) {
269 			ERROR("Can't open %s for reading: %s\n", infile,
270 			      strerror(errno));
271 			return FILE_ERR_OPEN;
272 		}
273 	}
274 	return FILE_ERR_NONE;
275 }
276 
futil_close_file(int fd)277 enum futil_file_err futil_close_file(int fd)
278 {
279 	if (fd >= 0 && close(fd)) {
280 		ERROR("Closing ifd: %s\n", strerror(errno));
281 		return FILE_ERR_CLOSE;
282 	}
283 	return FILE_ERR_NONE;
284 }
285 
futil_map_file(int fd,enum file_mode mode,uint8_t ** buf,uint32_t * len)286 enum futil_file_err futil_map_file(int fd, enum file_mode mode,
287 				   uint8_t **buf, uint32_t *len)
288 {
289 	struct stat sb;
290 	void *mmap_ptr;
291 	uint32_t reasonable_len;
292 
293 	if (0 != fstat(fd, &sb)) {
294 		ERROR("Can't stat input file: %s\n", strerror(errno));
295 		return FILE_ERR_STAT;
296 	}
297 
298 #if !defined(HAVE_MACOS) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
299 	if (S_ISBLK(sb.st_mode))
300 		ioctl(fd, BLKGETSIZE64, &sb.st_size);
301 #endif
302 
303 	/* If the image is larger than 2^32 bytes, it's wrong. */
304 	if (sb.st_size < 0 || sb.st_size > UINT32_MAX) {
305 		ERROR("Image size is unreasonable\n");
306 		return FILE_ERR_SIZE;
307 	}
308 	reasonable_len = (uint32_t)sb.st_size;
309 
310 	if (mode == FILE_RW)
311 		mmap_ptr = mmap(0, sb.st_size,
312 				PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
313 	else
314 		mmap_ptr = mmap(0, sb.st_size,
315 				PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
316 
317 	if (mmap_ptr == MAP_FAILED) {
318 		ERROR("Can't mmap %s file: %s\n",
319 		      mode == FILE_RW ? "output" : "input", strerror(errno));
320 		return FILE_ERR_MMAP;
321 	}
322 
323 	*buf = (uint8_t *)mmap_ptr;
324 	*len = reasonable_len;
325 	return FILE_ERR_NONE;
326 }
327 
futil_unmap_file(int fd,enum file_mode mode,uint8_t * buf,uint32_t len)328 enum futil_file_err futil_unmap_file(int fd, enum file_mode mode,
329 				     uint8_t *buf, uint32_t len)
330 {
331 	void *mmap_ptr = buf;
332 	enum futil_file_err err = FILE_ERR_NONE;
333 
334 	if (mode == FILE_RW &&
335 	    (0 != msync(mmap_ptr, len, MS_SYNC | MS_INVALIDATE))) {
336 		ERROR("msync failed: %s\n", strerror(errno));
337 		err = FILE_ERR_MSYNC;
338 	}
339 
340 	if (0 != munmap(mmap_ptr, len)) {
341 		ERROR("Can't munmap pointer: %s\n", strerror(errno));
342 		if (err == FILE_ERR_NONE)
343 			err = FILE_ERR_MUNMAP;
344 	}
345 
346 	return err;
347 }
348 
futil_open_and_map_file(const char * infile,int * fd,enum file_mode mode,uint8_t ** buf,uint32_t * len)349 enum futil_file_err futil_open_and_map_file(const char *infile, int *fd,
350 					    enum file_mode mode, uint8_t **buf,
351 					    uint32_t *len)
352 {
353 	enum futil_file_err rv = futil_open_file(infile, fd, mode);
354 	if (rv != FILE_ERR_NONE)
355 		return rv;
356 
357 	rv = futil_map_file(*fd, mode,  buf, len);
358 	if (rv != FILE_ERR_NONE)
359 		futil_close_file(*fd);
360 
361 	return rv;
362 }
363 
futil_unmap_and_close_file(int fd,enum file_mode mode,uint8_t * buf,uint32_t len)364 enum futil_file_err futil_unmap_and_close_file(int fd, enum file_mode mode,
365 					       uint8_t *buf, uint32_t len)
366 {
367 	enum futil_file_err rv = FILE_ERR_NONE;
368 
369 	if (buf)
370 		rv = futil_unmap_file(fd, mode, buf, len);
371 	if (rv != FILE_ERR_NONE)
372 		return rv;
373 
374 	if (fd != -1)
375 		return futil_close_file(fd);
376 	else
377 		return FILE_ERR_NONE;
378 }
379 
380 #define DISK_SECTOR_SIZE 512
ft_recognize_gpt(uint8_t * buf,uint32_t len)381 enum futil_file_type ft_recognize_gpt(uint8_t *buf, uint32_t len)
382 {
383 	GptHeader *h;
384 
385 	/* GPT header starts at sector 1, is one sector long */
386 	if (len < 2 * DISK_SECTOR_SIZE)
387 		return FILE_TYPE_UNKNOWN;
388 
389 	h = (GptHeader *)(buf + DISK_SECTOR_SIZE);
390 
391 	if (memcmp(h->signature, GPT_HEADER_SIGNATURE,
392 		   GPT_HEADER_SIGNATURE_SIZE) &&
393 	    memcmp(h->signature, GPT_HEADER_SIGNATURE2,
394 		   GPT_HEADER_SIGNATURE_SIZE))
395 		return FILE_TYPE_UNKNOWN;
396 	if (h->revision != GPT_HEADER_REVISION)
397 		return FILE_TYPE_UNKNOWN;
398 	if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER)
399 		return FILE_TYPE_UNKNOWN;
400 
401 	if (HeaderCrc(h) != h->header_crc32)
402 		return FILE_TYPE_UNKNOWN;
403 
404 	return FILE_TYPE_CHROMIUMOS_DISK;
405 }
406 
parse_digest_or_die(uint8_t * buf,int len,const char * str)407 void parse_digest_or_die(uint8_t *buf, int len, const char *str)
408 {
409 	if (!parse_hash(buf, len, str)) {
410 		ERROR("Invalid DIGEST \"%s\"\n", str);
411 		exit(1);
412 	}
413 }
414 
print_bytes(const void * ptr,size_t len)415 void print_bytes(const void *ptr, size_t len)
416 {
417 	const uint8_t *buf = (const uint8_t *)ptr;
418 
419 	for (size_t i = 0; i < len; i++)
420 		printf("%02x", *buf++);
421 }
422 
write_to_file(const char * msg,const char * filename,uint8_t * start,size_t size)423 int write_to_file(const char *msg, const char *filename, uint8_t *start,
424 		  size_t size)
425 {
426 	FILE *fp;
427 	int r = 0;
428 
429 	fp = fopen(filename, "wb");
430 	if (!fp) {
431 		r = errno;
432 		ERROR("Unable to open %s for writing: %s\n", filename,
433 		      strerror(r));
434 		return r;
435 	}
436 
437 	/* Don't write zero bytes */
438 	if (size && 1 != fwrite(start, size, 1, fp)) {
439 		r = errno;
440 		ERROR("Unable to write to %s: %s\n", filename, strerror(r));
441 	}
442 
443 	if (fclose(fp) != 0) {
444 		int e = errno;
445 		ERROR("Unable to close %s: %s\n", filename, strerror(e));
446 		if (!r)
447 			r = e;
448 	}
449 
450 	if (!r && msg)
451 		printf("%s %s\n", msg, filename);
452 
453 	return r;
454 }
455