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
7#include <errno.h>
8#ifndef HAVE_MACOS
9#include <linux/fs.h>		/* For BLKGETSIZE64 */
10#endif
11#include <stdarg.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/ioctl.h>
17#include <sys/mman.h>
18#include <sys/stat.h>
19#include <sys/types.h>
20#include <sys/wait.h>
21#include <unistd.h>
22
23#include "cgptlib_internal.h"
24#include "file_type.h"
25#include "futility.h"
26#include "gbb_header.h"
27
28int debugging_enabled;
29void Debug(const char *format, ...)
30{
31	if (!debugging_enabled)
32		return;
33
34	va_list ap;
35	va_start(ap, format);
36	fprintf(stderr, "DEBUG: ");
37	vfprintf(stderr, format, ap);
38	va_end(ap);
39}
40
41static int is_null_terminated(const char *s, int len)
42{
43	len--;
44	s += len;
45	while (len-- >= 0)
46		if (!*s--)
47			return 1;
48	return 0;
49}
50
51static inline uint32_t max(uint32_t a, uint32_t b)
52{
53	return a > b ? a : b;
54}
55
56enum futil_file_type recognize_gbb(uint8_t *buf, uint32_t len)
57{
58	GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf;
59
60	if (memcmp(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE))
61		return FILE_TYPE_UNKNOWN;
62	if (gbb->major_version > GBB_MAJOR_VER)
63		return FILE_TYPE_UNKNOWN;
64	if (sizeof(GoogleBinaryBlockHeader) > len)
65		return FILE_TYPE_UNKNOWN;
66
67	/* close enough */
68	return FILE_TYPE_GBB;
69}
70
71int futil_valid_gbb_header(GoogleBinaryBlockHeader *gbb, uint32_t len,
72			   uint32_t *maxlen_ptr)
73{
74	if (len < sizeof(GoogleBinaryBlockHeader))
75		return 0;
76
77	if (memcmp(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE))
78		return 0;
79	if (gbb->major_version != GBB_MAJOR_VER)
80		return 0;
81
82	/* Check limits first, to help identify problems */
83	if (maxlen_ptr) {
84		uint32_t maxlen = gbb->header_size;
85		maxlen = max(maxlen,
86			     gbb->hwid_offset + gbb->hwid_size);
87		maxlen = max(maxlen,
88			     gbb->rootkey_offset + gbb->rootkey_size);
89		maxlen = max(maxlen,
90			     gbb->bmpfv_offset + gbb->bmpfv_size);
91		maxlen = max(maxlen,
92			     gbb->recovery_key_offset + gbb->recovery_key_size);
93		*maxlen_ptr = maxlen;
94	}
95
96	if (gbb->header_size != GBB_HEADER_SIZE || gbb->header_size > len)
97		return 0;
98	if (gbb->hwid_offset < GBB_HEADER_SIZE)
99		return 0;
100	if (gbb->hwid_offset + gbb->hwid_size > len)
101		return 0;
102	if (gbb->hwid_size) {
103		const char *s = (const char *)
104			((uint8_t *)gbb + gbb->hwid_offset);
105		if (!is_null_terminated(s, gbb->hwid_size))
106			return 0;
107	}
108	if (gbb->rootkey_offset < GBB_HEADER_SIZE)
109		return 0;
110	if (gbb->rootkey_offset + gbb->rootkey_size > len)
111		return 0;
112
113	if (gbb->bmpfv_offset < GBB_HEADER_SIZE)
114		return 0;
115	if (gbb->bmpfv_offset + gbb->bmpfv_size > len)
116		return 0;
117	if (gbb->recovery_key_offset < GBB_HEADER_SIZE)
118		return 0;
119	if (gbb->recovery_key_offset + gbb->recovery_key_size > len)
120		return 0;
121
122	/* Seems legit... */
123	return 1;
124}
125
126/* For GBB v1.2 and later, print the stored digest of the HWID (and whether
127 * it's correct). Return true if it is correct. */
128int print_hwid_digest(GoogleBinaryBlockHeader *gbb,
129		      const char *banner, const char *footer)
130{
131	printf("%s", banner);
132
133	/* There isn't one for v1.1 and earlier, so assume it's good. */
134	if (gbb->minor_version < 2) {
135		printf("<none>%s", footer);
136		return 1;
137	}
138
139	uint8_t *buf = (uint8_t *)gbb;
140	char *hwid_str = (char *)(buf + gbb->hwid_offset);
141	int is_valid = 0;
142	uint8_t *digest = DigestBuf(buf + gbb->hwid_offset,
143				    strlen(hwid_str),
144				    SHA256_DIGEST_ALGORITHM);
145	if (digest) {
146		int i;
147		is_valid = 1;
148		/* print it, comparing as we go */
149		for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
150			printf("%02x", gbb->hwid_digest[i]);
151			if (gbb->hwid_digest[i] != digest[i])
152				is_valid = 0;
153		}
154		free(digest);
155	}
156
157	printf("   %s", is_valid ? "valid" : "<invalid>");
158	printf("%s", footer);
159	return is_valid;
160}
161
162/* For GBB v1.2 and later, update the hwid_digest field. */
163void update_hwid_digest(GoogleBinaryBlockHeader *gbb)
164{
165	/* There isn't one for v1.1 and earlier */
166	if (gbb->minor_version < 2)
167		return;
168
169	uint8_t *buf = (uint8_t *)gbb;
170	char *hwid_str = (char *)(buf + gbb->hwid_offset);
171	uint8_t *digest = DigestBuf(buf + gbb->hwid_offset,
172				    strlen(hwid_str),
173				    SHA256_DIGEST_ALGORITHM);
174	memcpy(gbb->hwid_digest, digest, SHA256_DIGEST_SIZE);
175	free(digest);
176}
177
178/*
179 * TODO: All sorts of race conditions likely here, and everywhere this is used.
180 * Do we care? If so, fix it.
181 */
182void futil_copy_file_or_die(const char *infile, const char *outfile)
183{
184	pid_t pid;
185	int status;
186
187	Debug("%s(%s, %s)\n", __func__, infile, outfile);
188
189	pid = fork();
190
191	if (pid < 0) {
192		fprintf(stderr, "Couldn't fork /bin/cp process: %s\n",
193			strerror(errno));
194		exit(1);
195	}
196
197	/* child */
198	if (!pid) {
199		execl("/bin/cp", "/bin/cp", infile, outfile, NULL);
200		fprintf(stderr, "Child couldn't exec /bin/cp: %s\n",
201			strerror(errno));
202		exit(1);
203	}
204
205	/* parent - wait for child to finish */
206	if (wait(&status) == -1) {
207		fprintf(stderr,
208			"Couldn't wait for /bin/cp process to exit: %s\n",
209			strerror(errno));
210		exit(1);
211	}
212
213	if (WIFEXITED(status)) {
214		status = WEXITSTATUS(status);
215		/* zero is normal exit */
216		if (!status)
217			return;
218		fprintf(stderr, "/bin/cp exited with status %d\n", status);
219		exit(1);
220	}
221
222	if (WIFSIGNALED(status)) {
223		status = WTERMSIG(status);
224		fprintf(stderr, "/bin/cp was killed with signal %d\n", status);
225		exit(1);
226	}
227
228	fprintf(stderr, "I have no idea what just happened\n");
229	exit(1);
230}
231
232
233enum futil_file_err futil_map_file(int fd, int writeable,
234				   uint8_t **buf, uint32_t *len)
235{
236	struct stat sb;
237	void *mmap_ptr;
238	uint32_t reasonable_len;
239
240	if (0 != fstat(fd, &sb)) {
241		fprintf(stderr, "Can't stat input file: %s\n",
242			strerror(errno));
243		return FILE_ERR_STAT;
244	}
245
246#ifndef HAVE_MACOS
247	if (S_ISBLK(sb.st_mode))
248		ioctl(fd, BLKGETSIZE64, &sb.st_size);
249#endif
250
251	/* If the image is larger than 2^32 bytes, it's wrong. */
252	if (sb.st_size < 0 || sb.st_size > UINT32_MAX) {
253		fprintf(stderr, "Image size is unreasonable\n");
254		return FILE_ERR_SIZE;
255	}
256	reasonable_len = (uint32_t)sb.st_size;
257
258	if (writeable)
259		mmap_ptr = mmap(0, sb.st_size,
260				PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
261	else
262		mmap_ptr = mmap(0, sb.st_size,
263				PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
264
265	if (mmap_ptr == (void *)-1) {
266		fprintf(stderr, "Can't mmap %s file: %s\n",
267			writeable ? "output" : "input",
268			strerror(errno));
269		return FILE_ERR_MMAP;
270	}
271
272	*buf = (uint8_t *)mmap_ptr;
273	*len = reasonable_len;
274	return FILE_ERR_NONE;
275}
276
277enum futil_file_err futil_unmap_file(int fd, int writeable,
278				     uint8_t *buf, uint32_t len)
279{
280	void *mmap_ptr = buf;
281	enum futil_file_err err = FILE_ERR_NONE;
282
283	if (writeable &&
284	    (0 != msync(mmap_ptr, len, MS_SYNC|MS_INVALIDATE))) {
285		fprintf(stderr, "msync failed: %s\n", strerror(errno));
286		err = FILE_ERR_MSYNC;
287	}
288
289	if (0 != munmap(mmap_ptr, len)) {
290		fprintf(stderr, "Can't munmap pointer: %s\n",
291			strerror(errno));
292		if (err == FILE_ERR_NONE)
293			err = FILE_ERR_MUNMAP;
294	}
295
296	return err;
297}
298
299
300#define DISK_SECTOR_SIZE 512
301enum futil_file_type recognize_gpt(uint8_t *buf, uint32_t len)
302{
303	GptHeader *h;
304
305	/* GPT header starts at sector 1, is one sector long */
306	if (len < 2 * DISK_SECTOR_SIZE)
307		return FILE_TYPE_UNKNOWN;
308
309	h = (GptHeader *)(buf + DISK_SECTOR_SIZE);
310
311	if (memcmp(h->signature, GPT_HEADER_SIGNATURE,
312		   GPT_HEADER_SIGNATURE_SIZE) &&
313	    memcmp(h->signature, GPT_HEADER_SIGNATURE2,
314		   GPT_HEADER_SIGNATURE_SIZE))
315		return FILE_TYPE_UNKNOWN;
316	if (h->revision != GPT_HEADER_REVISION)
317		return FILE_TYPE_UNKNOWN;
318	if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER)
319		return FILE_TYPE_UNKNOWN;
320
321	if (HeaderCrc(h) != h->header_crc32)
322		return FILE_TYPE_UNKNOWN;
323
324	return FILE_TYPE_CHROMIUMOS_DISK;
325}
326