1 /*
2  * Copyright 2014 The Chromium OS Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6 #include <errno.h>
7 #include <getopt.h>
8 #include <inttypes.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 
18 #include "futility.h"
19 #include "gbb_header.h"
20 
print_help(const char * prog)21 static void print_help(const char *prog)
22 {
23 	printf("\n"
24 		"Usage:  " MYNAME " %s [-g|-s|-c] [OPTIONS] "
25 	       "bios_file [output_file]\n"
26 		"\n"
27 		"GET MODE:\n"
28 		"-g, --get   (default)\tGet (read) from bios_file, "
29 		"with following options:\n"
30 		"     --hwid          \tReport hardware id (default).\n"
31 		"     --flags         \tReport header flags.\n"
32 		"     --digest        \tReport digest of hwid (>= v1.2)\n"
33 		" -k, --rootkey=FILE  \tFile name to export Root Key.\n"
34 		" -b, --bmpfv=FILE    \tFile name to export Bitmap FV.\n"
35 		" -r  --recoverykey=FILE\tFile name to export Recovery Key.\n"
36 		"\n"
37 		"SET MODE:\n"
38 		"-s, --set            \tSet (write) to bios_file, "
39 		"with following options:\n"
40 		" -o, --output=FILE   \tNew file name for ouptput.\n"
41 		"     --hwid=HWID     \tThe new hardware id to be changed.\n"
42 		"     --flags=FLAGS   \tThe new (numeric) flags value.\n"
43 		" -k, --rootkey=FILE  \tFile name of new Root Key.\n"
44 		" -b, --bmpfv=FILE    \tFile name of new Bitmap FV.\n"
45 		" -r  --recoverykey=FILE\tFile name of new Recovery Key.\n"
46 		"\n"
47 		"CREATE MODE:\n"
48 		"-c, --create=hwid_size,rootkey_size,bmpfv_size,"
49 		"recoverykey_size\n"
50 		"                     \tCreate a GBB blob by given size list.\n"
51 		"SAMPLE:\n"
52 		"  %s -g bios.bin\n"
53 		"  %s --set --hwid='New Model' -k key.bin"
54 		" bios.bin newbios.bin\n"
55 		"  %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n\n",
56 		prog, prog, prog, prog);
57 }
58 
59 enum {
60 	OPT_HWID = 1000,
61 	OPT_FLAGS,
62 	OPT_DIGEST,
63 };
64 
65 /* Command line options */
66 static struct option long_opts[] = {
67 	/* name  has_arg *flag val */
68 	{"get", 0, NULL, 'g'},
69 	{"set", 0, NULL, 's'},
70 	{"create", 1, NULL, 'c'},
71 	{"output", 1, NULL, 'o'},
72 	{"rootkey", 1, NULL, 'k'},
73 	{"bmpfv", 1, NULL, 'b'},
74 	{"recoverykey", 1, NULL, 'r'},
75 	{"hwid", 0, NULL, OPT_HWID},
76 	{"flags", 0, NULL, OPT_FLAGS},
77 	{"digest", 0, NULL, OPT_DIGEST},
78 	{NULL, 0, NULL, 0},
79 };
80 
81 static char *short_opts = ":gsc:o:k:b:r:";
82 
83 /* Change the has_arg field of a long_opts entry */
opt_has_arg(const char * name,int val)84 static void opt_has_arg(const char *name, int val)
85 {
86 	struct option *p;
87 	for (p = long_opts; p->name; p++)
88 		if (!strcmp(name, p->name)) {
89 			p->has_arg = val;
90 			break;
91 		}
92 }
93 
94 static int errorcnt;
95 
96 #define GBB_SEARCH_STRIDE 4
FindGbbHeader(uint8_t * ptr,size_t size)97 static GoogleBinaryBlockHeader *FindGbbHeader(uint8_t *ptr, size_t size)
98 {
99 	size_t i;
100 	GoogleBinaryBlockHeader *tmp, *gbb_header = NULL;
101 	int count = 0;
102 
103 	for (i = 0; i <= size - GBB_SEARCH_STRIDE; i += GBB_SEARCH_STRIDE) {
104 		if (0 != memcmp(ptr + i, GBB_SIGNATURE, GBB_SIGNATURE_SIZE))
105 			continue;
106 
107 		/* Found something. See if it's any good. */
108 		tmp = (GoogleBinaryBlockHeader *) (ptr + i);
109 		if (futil_valid_gbb_header(tmp, size - i, NULL))
110 			if (!count++)
111 				gbb_header = tmp;
112 	}
113 
114 	switch (count) {
115 	case 0:
116 		errorcnt++;
117 		return NULL;
118 	case 1:
119 		return gbb_header;
120 	default:
121 		fprintf(stderr, "ERROR: multiple GBB headers found\n");
122 		errorcnt++;
123 		return NULL;
124 	}
125 }
126 
create_gbb(const char * desc,off_t * sizeptr)127 static uint8_t *create_gbb(const char *desc, off_t *sizeptr)
128 {
129 	char *str, *sizes, *param, *e = NULL;
130 	size_t size = GBB_HEADER_SIZE;
131 	int i = 0;
132 	/* Danger Will Robinson! four entries ==> four paramater blocks */
133 	uint32_t val[] = { 0, 0, 0, 0 };
134 	uint8_t *buf;
135 	GoogleBinaryBlockHeader *gbb;
136 
137 	sizes = strdup(desc);
138 	if (!sizes) {
139 		errorcnt++;
140 		fprintf(stderr, "ERROR: strdup() failed: %s\n",
141 			strerror(errno));
142 		return NULL;
143 	}
144 
145 	for (str = sizes; (param = strtok(str, ", ")) != NULL; str = NULL) {
146 		val[i] = (uint32_t) strtoul(param, &e, 0);
147 		if (e && *e) {
148 			errorcnt++;
149 			fprintf(stderr,
150 				"ERROR: invalid creation parameter: \"%s\"\n",
151 				param);
152 			free(sizes);
153 			return NULL;
154 		}
155 		size += val[i++];
156 		if (i > ARRAY_SIZE(val))
157 			break;
158 	}
159 
160 	buf = (uint8_t *) calloc(1, size);
161 	if (!buf) {
162 		errorcnt++;
163 		fprintf(stderr, "ERROR: can't malloc %zu bytes: %s\n",
164 			size, strerror(errno));
165 		free(sizes);
166 		return NULL;
167 	} else if (sizeptr) {
168 		*sizeptr = size;
169 	}
170 
171 	gbb = (GoogleBinaryBlockHeader *) buf;
172 	memcpy(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE);
173 	gbb->major_version = GBB_MAJOR_VER;
174 	gbb->minor_version = GBB_MINOR_VER;
175 	gbb->header_size = GBB_HEADER_SIZE;
176 	gbb->flags = 0;
177 
178 	i = GBB_HEADER_SIZE;
179 	gbb->hwid_offset = i;
180 	gbb->hwid_size = val[0];
181 	i += val[0];
182 
183 	gbb->rootkey_offset = i;
184 	gbb->rootkey_size = val[1];
185 	i += val[1];
186 
187 	gbb->bmpfv_offset = i;
188 	gbb->bmpfv_size = val[2];
189 	i += val[2];
190 
191 	gbb->recovery_key_offset = i;
192 	gbb->recovery_key_size = val[3];
193 	i += val[1];
194 
195 	free(sizes);
196 	return buf;
197 }
198 
read_entire_file(const char * filename,off_t * sizeptr)199 static uint8_t *read_entire_file(const char *filename, off_t *sizeptr)
200 {
201 	FILE *fp = NULL;
202 	uint8_t *buf = NULL;
203 	struct stat sb;
204 
205 	fp = fopen(filename, "rb");
206 	if (!fp) {
207 		fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n",
208 			filename, strerror(errno));
209 		goto fail;
210 	}
211 
212 	if (0 != fstat(fileno(fp), &sb)) {
213 		fprintf(stderr, "ERROR: can't fstat %s: %s\n",
214 			filename, strerror(errno));
215 		goto fail;
216 	}
217 	if (sizeptr)
218 		*sizeptr = sb.st_size;
219 
220 	buf = (uint8_t *) malloc(sb.st_size);
221 	if (!buf) {
222 		fprintf(stderr, "ERROR: can't malloc %" PRIi64 " bytes: %s\n",
223 			sb.st_size, strerror(errno));
224 		goto fail;
225 	}
226 
227 	if (1 != fread(buf, sb.st_size, 1, fp)) {
228 		fprintf(stderr, "ERROR: Unable to read from %s: %s\n",
229 			filename, strerror(errno));
230 		goto fail;
231 	}
232 
233 	if (fp && 0 != fclose(fp)) {
234 		fprintf(stderr, "ERROR: Unable to close %s: %s\n",
235 			filename, strerror(errno));
236 		goto fail;
237 	}
238 
239 	return buf;
240 
241 fail:
242 	errorcnt++;
243 
244 	if (buf)
245 		free(buf);
246 
247 	if (fp && 0 != fclose(fp))
248 		fprintf(stderr, "ERROR: Unable to close %s: %s\n",
249 			filename, strerror(errno));
250 	return NULL;
251 }
252 
write_to_file(const char * msg,const char * filename,uint8_t * start,size_t size)253 static int write_to_file(const char *msg, const char *filename,
254 			 uint8_t *start, size_t size)
255 {
256 	FILE *fp;
257 	int r = 0;
258 
259 	fp = fopen(filename, "wb");
260 	if (!fp) {
261 		fprintf(stderr, "ERROR: Unable to open %s for writing: %s\n",
262 			filename, strerror(errno));
263 		errorcnt++;
264 		return errno;
265 	}
266 
267 	/* Don't write zero bytes */
268 	if (size && 1 != fwrite(start, size, 1, fp)) {
269 		fprintf(stderr, "ERROR: Unable to write to %s: %s\n",
270 			filename, strerror(errno));
271 		errorcnt++;
272 		r = errno;
273 	}
274 
275 	if (0 != fclose(fp)) {
276 		fprintf(stderr, "ERROR: Unable to close %s: %s\n",
277 			filename, strerror(errno));
278 		errorcnt++;
279 		if (!r)
280 			r = errno;
281 	}
282 
283 	if (!r && msg)
284 		printf("%s %s\n", msg, filename);
285 
286 	return r;
287 }
288 
read_from_file(const char * msg,const char * filename,uint8_t * start,uint32_t size)289 static int read_from_file(const char *msg, const char *filename,
290 			  uint8_t *start, uint32_t size)
291 {
292 	FILE *fp;
293 	struct stat sb;
294 	size_t count;
295 	int r = 0;
296 
297 	fp = fopen(filename, "rb");
298 	if (!fp) {
299 		fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n",
300 			filename, strerror(errno));
301 		errorcnt++;
302 		return errno;
303 	}
304 
305 	if (0 != fstat(fileno(fp), &sb)) {
306 		fprintf(stderr, "ERROR: can't fstat %s: %s\n",
307 			filename, strerror(errno));
308 		errorcnt++;
309 		r = errno;
310 		goto done_close;
311 	}
312 
313 	if (sb.st_size > size) {
314 		fprintf(stderr,
315 			"ERROR: file %s exceeds capacity (%" PRIu32 ")\n",
316 			filename, size);
317 		errorcnt++;
318 		r = errno;
319 		goto done_close;
320 	}
321 
322 	/* Wipe existing data. */
323 	memset(start, 0, size);
324 
325 	/* It's okay if we read less than size. That's just the max. */
326 	count = fread(start, 1, size, fp);
327 	if (ferror(fp)) {
328 		fprintf(stderr,
329 			"ERROR: Read %zu/%" PRIi64 " bytes from %s: %s\n",
330 			count, sb.st_size, filename, strerror(errno));
331 		errorcnt++;
332 		r = errno;
333 	}
334 
335 done_close:
336 	if (0 != fclose(fp)) {
337 		fprintf(stderr, "ERROR: Unable to close %s: %s\n",
338 			filename, strerror(errno));
339 		errorcnt++;
340 		if (!r)
341 			r = errno;
342 	}
343 
344 	if (!r && msg)
345 		printf(" - import %s from %s: success\n", msg, filename);
346 
347 	return r;
348 }
349 
do_gbb_utility(int argc,char * argv[])350 static int do_gbb_utility(int argc, char *argv[])
351 {
352 	enum do_what_now { DO_GET, DO_SET, DO_CREATE } mode = DO_GET;
353 	char *infile = NULL;
354 	char *outfile = NULL;
355 	char *opt_create = NULL;
356 	char *opt_rootkey = NULL;
357 	char *opt_bmpfv = NULL;
358 	char *opt_recoverykey = NULL;
359 	char *opt_hwid = NULL;
360 	char *opt_flags = NULL;
361 	int sel_hwid = 0;
362 	int sel_digest = 0;
363 	int sel_flags = 0;
364 	uint8_t *inbuf = NULL;
365 	off_t filesize;
366 	uint8_t *outbuf = NULL;
367 	GoogleBinaryBlockHeader *gbb;
368 	uint8_t *gbb_base;
369 	int i;
370 
371 	opterr = 0;		/* quiet, you */
372 	while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
373 		switch (i) {
374 		case 'g':
375 			mode = DO_GET;
376 			opt_has_arg("flags", 0);
377 			opt_has_arg("hwid", 0);
378 			break;
379 		case 's':
380 			mode = DO_SET;
381 			opt_has_arg("flags", 1);
382 			opt_has_arg("hwid", 1);
383 			break;
384 		case 'c':
385 			mode = DO_CREATE;
386 			opt_create = optarg;
387 			break;
388 		case 'o':
389 			outfile = optarg;
390 			break;
391 		case 'k':
392 			opt_rootkey = optarg;
393 			break;
394 		case 'b':
395 			opt_bmpfv = optarg;
396 			break;
397 		case 'r':
398 			opt_recoverykey = optarg;
399 			break;
400 		case OPT_HWID:
401 			/* --hwid is optional: null might be okay */
402 			opt_hwid = optarg;
403 			sel_hwid = 1;
404 			break;
405 		case OPT_FLAGS:
406 			/* --flags is optional: null might be okay */
407 			opt_flags = optarg;
408 			sel_flags = 1;
409 			break;
410 		case OPT_DIGEST:
411 			sel_digest = 1;
412 			break;
413 		case '?':
414 			errorcnt++;
415 			if (optopt)
416 				fprintf(stderr,
417 					"ERROR: unrecognized option: -%c\n",
418 					optopt);
419 			else if (argv[optind - 1])
420 				fprintf(stderr,
421 					"ERROR: unrecognized option "
422 					"(possibly \"%s\")\n",
423 					argv[optind - 1]);
424 			else
425 				fprintf(stderr, "ERROR: unrecognized option\n");
426 			break;
427 		case ':':
428 			errorcnt++;
429 			if (argv[optind - 1])
430 				fprintf(stderr,
431 					"ERROR: missing argument to -%c (%s)\n",
432 					optopt, argv[optind - 1]);
433 			else
434 				fprintf(stderr,
435 					"ERROR: missing argument to -%c\n",
436 					optopt);
437 			break;
438 		default:
439 			errorcnt++;
440 			fprintf(stderr,
441 				"ERROR: error while parsing options\n");
442 		}
443 	}
444 
445 	/* Problems? */
446 	if (errorcnt) {
447 		print_help(argv[0]);
448 		return 1;
449 	}
450 
451 	/* Now try to do something */
452 	switch (mode) {
453 	case DO_GET:
454 		if (argc - optind < 1) {
455 			fprintf(stderr, "\nERROR: missing input filename\n");
456 			print_help(argv[0]);
457 			return 1;
458 		} else {
459 			infile = argv[optind++];
460 		}
461 
462 		/* With no args, show the HWID */
463 		if (!opt_rootkey && !opt_bmpfv && !opt_recoverykey
464 		    && !sel_flags && !sel_digest)
465 			sel_hwid = 1;
466 
467 		inbuf = read_entire_file(infile, &filesize);
468 		if (!inbuf)
469 			break;
470 
471 		gbb = FindGbbHeader(inbuf, filesize);
472 		if (!gbb) {
473 			fprintf(stderr, "ERROR: No GBB found in %s\n", infile);
474 			break;
475 		}
476 		gbb_base = (uint8_t *) gbb;
477 
478 		/* Get the stuff */
479 		if (sel_hwid)
480 			printf("hardware_id: %s\n",
481 			       gbb->hwid_size ? (char *)(gbb_base +
482 							 gbb->
483 							 hwid_offset) : "");
484 		if (sel_digest)
485 			print_hwid_digest(gbb, "digest: ", "\n");
486 
487 		if (sel_flags)
488 			printf("flags: 0x%08x\n", gbb->flags);
489 		if (opt_rootkey)
490 			write_to_file(" - exported root_key to file:",
491 				      opt_rootkey,
492 				      gbb_base + gbb->rootkey_offset,
493 				      gbb->rootkey_size);
494 		if (opt_bmpfv)
495 			write_to_file(" - exported bmp_fv to file:", opt_bmpfv,
496 				      gbb_base + gbb->bmpfv_offset,
497 				      gbb->bmpfv_size);
498 		if (opt_recoverykey)
499 			write_to_file(" - exported recovery_key to file:",
500 				      opt_recoverykey,
501 				      gbb_base + gbb->recovery_key_offset,
502 				      gbb->recovery_key_size);
503 		break;
504 
505 	case DO_SET:
506 		if (argc - optind < 1) {
507 			fprintf(stderr, "\nERROR: missing input filename\n");
508 			print_help(argv[0]);
509 			return 1;
510 		}
511 		infile = argv[optind++];
512 		if (!outfile)
513 			outfile = (argc - optind < 1) ? infile : argv[optind++];
514 
515 		if (sel_hwid && !opt_hwid) {
516 			fprintf(stderr, "\nERROR: missing new HWID value\n");
517 			print_help(argv[0]);
518 			return 1;
519 		}
520 		if (sel_flags && (!opt_flags || !*opt_flags)) {
521 			fprintf(stderr, "\nERROR: missing new flags value\n");
522 			print_help(argv[0]);
523 			return 1;
524 		}
525 
526 		/* With no args, we'll either copy it unchanged or do nothing */
527 		inbuf = read_entire_file(infile, &filesize);
528 		if (!inbuf)
529 			break;
530 
531 		gbb = FindGbbHeader(inbuf, filesize);
532 		if (!gbb) {
533 			fprintf(stderr, "ERROR: No GBB found in %s\n", infile);
534 			break;
535 		}
536 		gbb_base = (uint8_t *) gbb;
537 
538 		outbuf = (uint8_t *) malloc(filesize);
539 		if (!outbuf) {
540 			errorcnt++;
541 			fprintf(stderr,
542 				"ERROR: can't malloc %" PRIi64 " bytes: %s\n",
543 				filesize, strerror(errno));
544 			break;
545 		}
546 
547 		/* Switch pointers to outbuf */
548 		memcpy(outbuf, inbuf, filesize);
549 		gbb = FindGbbHeader(outbuf, filesize);
550 		if (!gbb) {
551 			fprintf(stderr,
552 				"INTERNAL ERROR: No GBB found in outbuf\n");
553 			exit(1);
554 		}
555 		gbb_base = (uint8_t *) gbb;
556 
557 		if (opt_hwid) {
558 			if (strlen(opt_hwid) + 1 > gbb->hwid_size) {
559 				fprintf(stderr,
560 					"ERROR: null-terminated HWID"
561 					" exceeds capacity (%d)\n",
562 					gbb->hwid_size);
563 				errorcnt++;
564 			} else {
565 				/* Wipe data before writing new value. */
566 				memset(gbb_base + gbb->hwid_offset, 0,
567 				       gbb->hwid_size);
568 				strcpy((char *)(gbb_base + gbb->hwid_offset),
569 				       opt_hwid);
570 				update_hwid_digest(gbb);
571 			}
572 		}
573 
574 		if (opt_flags) {
575 			char *e = NULL;
576 			uint32_t val;
577 			val = (uint32_t) strtoul(opt_flags, &e, 0);
578 			if (e && *e) {
579 				fprintf(stderr,
580 					"ERROR: invalid flags value: %s\n",
581 					opt_flags);
582 				errorcnt++;
583 			} else {
584 				gbb->flags = val;
585 			}
586 		}
587 
588 		if (opt_rootkey)
589 			read_from_file("root_key", opt_rootkey,
590 				       gbb_base + gbb->rootkey_offset,
591 				       gbb->rootkey_size);
592 		if (opt_bmpfv)
593 			read_from_file("bmp_fv", opt_bmpfv,
594 				       gbb_base + gbb->bmpfv_offset,
595 				       gbb->bmpfv_size);
596 		if (opt_recoverykey)
597 			read_from_file("recovery_key", opt_recoverykey,
598 				       gbb_base + gbb->recovery_key_offset,
599 				       gbb->recovery_key_size);
600 
601 		/* Write it out if there are no problems. */
602 		if (!errorcnt)
603 			write_to_file("successfully saved new image to:",
604 				      outfile, outbuf, filesize);
605 
606 		break;
607 
608 	case DO_CREATE:
609 		if (!outfile) {
610 			if (argc - optind < 1) {
611 				fprintf(stderr,
612 					"\nERROR: missing output filename\n");
613 				print_help(argv[0]);
614 				return 1;
615 			}
616 			outfile = argv[optind++];
617 		}
618 		/* Parse the creation args */
619 		outbuf = create_gbb(opt_create, &filesize);
620 		if (!outbuf) {
621 			fprintf(stderr,
622 				"\nERROR: unable to parse creation spec (%s)\n",
623 				opt_create);
624 			print_help(argv[0]);
625 			return 1;
626 		}
627 		if (!errorcnt)
628 			write_to_file("successfully created new GBB to:",
629 				      outfile, outbuf, filesize);
630 		break;
631 	}
632 
633 	if (inbuf)
634 		free(inbuf);
635 	if (outbuf)
636 		free(outbuf);
637 	return !!errorcnt;
638 }
639 
640 DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb_utility,
641 		      VBOOT_VERSION_ALL,
642 		      "Manipulate the Google Binary Block (GBB)",
643 		      print_help);
644