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