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 
28 int debugging_enabled;
Debug(const char * format,...)29 void 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 
is_null_terminated(const char * s,int len)41 static 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 
max(uint32_t a,uint32_t b)51 static inline uint32_t max(uint32_t a, uint32_t b)
52 {
53 	return a > b ? a : b;
54 }
55 
recognize_gbb(uint8_t * buf,uint32_t len)56 enum 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 
futil_valid_gbb_header(GoogleBinaryBlockHeader * gbb,uint32_t len,uint32_t * maxlen_ptr)71 int 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. */
print_hwid_digest(GoogleBinaryBlockHeader * gbb,const char * banner,const char * footer)128 int 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. */
update_hwid_digest(GoogleBinaryBlockHeader * gbb)163 void 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  */
futil_copy_file_or_die(const char * infile,const char * outfile)182 void 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 
futil_map_file(int fd,int writeable,uint8_t ** buf,uint32_t * len)233 enum 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 
futil_unmap_file(int fd,int writeable,uint8_t * buf,uint32_t len)277 enum 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
recognize_gpt(uint8_t * buf,uint32_t len)301 enum 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