Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 7 | #include <errno.h> |
David Riley | 05987b1 | 2015-02-05 19:22:49 -0800 | [diff] [blame] | 8 | #ifndef HAVE_MACOS |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 9 | #include <linux/fs.h> /* For BLKGETSIZE64 */ |
David Riley | 05987b1 | 2015-02-05 19:22:49 -0800 | [diff] [blame] | 10 | #endif |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 11 | #include <stdarg.h> |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 12 | #include <stdint.h> |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 15 | #include <string.h> |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 16 | #include <sys/ioctl.h> |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 17 | #include <sys/mman.h> |
| 18 | #include <sys/stat.h> |
| 19 | #include <sys/types.h> |
| 20 | #include <sys/wait.h> |
| 21 | #include <unistd.h> |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 22 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 23 | #include "cgptlib_internal.h" |
| 24 | #include "file_type.h" |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 25 | #include "futility.h" |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 26 | #include "gbb_header.h" |
| 27 | |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 28 | int debugging_enabled; |
| 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 | |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | |
| 51 | static inline uint32_t max(uint32_t a, uint32_t b) |
| 52 | { |
| 53 | return a > b ? a : b; |
| 54 | } |
| 55 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 56 | enum futil_file_type recognize_gbb(uint8_t *buf, uint32_t len) |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 57 | { |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 58 | GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf; |
| 59 | |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 60 | if (memcmp(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE)) |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 61 | return FILE_TYPE_UNKNOWN; |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 62 | if (gbb->major_version > GBB_MAJOR_VER) |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 63 | return FILE_TYPE_UNKNOWN; |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 64 | if (sizeof(GoogleBinaryBlockHeader) > len) |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 65 | return FILE_TYPE_UNKNOWN; |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 66 | |
| 67 | /* close enough */ |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 68 | return FILE_TYPE_GBB; |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | int futil_valid_gbb_header(GoogleBinaryBlockHeader *gbb, uint32_t len, |
| 72 | uint32_t *maxlen_ptr) |
| 73 | { |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 74 | if (len < sizeof(GoogleBinaryBlockHeader)) |
| 75 | return 0; |
| 76 | |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | } |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 125 | |
Bill Richardson | 6df3e33 | 2014-10-02 18:50:33 -0700 | [diff] [blame] | 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. */ |
| 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; |
Bill Richardson | b406c10 | 2014-12-03 14:10:13 -0800 | [diff] [blame] | 142 | uint8_t *digest = DigestBuf(buf + gbb->hwid_offset, |
Bill Richardson | 6df3e33 | 2014-10-02 18:50:33 -0700 | [diff] [blame] | 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. */ |
| 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); |
Bill Richardson | b406c10 | 2014-12-03 14:10:13 -0800 | [diff] [blame] | 171 | uint8_t *digest = DigestBuf(buf + gbb->hwid_offset, |
Bill Richardson | 6df3e33 | 2014-10-02 18:50:33 -0700 | [diff] [blame] | 172 | strlen(hwid_str), |
| 173 | SHA256_DIGEST_ALGORITHM); |
| 174 | memcpy(gbb->hwid_digest, digest, SHA256_DIGEST_SIZE); |
| 175 | free(digest); |
| 176 | } |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 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 | */ |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 182 | void futil_copy_file_or_die(const char *infile, const char *outfile) |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 183 | { |
| 184 | pid_t pid; |
| 185 | int status; |
| 186 | |
Bill Richardson | b406c10 | 2014-12-03 14:10:13 -0800 | [diff] [blame] | 187 | Debug("%s(%s, %s)\n", __func__, infile, outfile); |
| 188 | |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 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 | |
Bill Richardson | 779796f | 2014-09-23 11:47:40 -0700 | [diff] [blame] | 222 | if (WIFSIGNALED(status)) { |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 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 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 233 | enum futil_file_err futil_map_file(int fd, int writeable, |
| 234 | uint8_t **buf, uint32_t *len) |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 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)); |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 243 | return FILE_ERR_STAT; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 244 | } |
| 245 | |
David Riley | 05987b1 | 2015-02-05 19:22:49 -0800 | [diff] [blame] | 246 | #ifndef HAVE_MACOS |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 247 | if (S_ISBLK(sb.st_mode)) |
| 248 | ioctl(fd, BLKGETSIZE64, &sb.st_size); |
David Riley | 05987b1 | 2015-02-05 19:22:49 -0800 | [diff] [blame] | 249 | #endif |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 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"); |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 254 | return FILE_ERR_SIZE; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 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, |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 263 | PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 264 | |
| 265 | if (mmap_ptr == (void *)-1) { |
| 266 | fprintf(stderr, "Can't mmap %s file: %s\n", |
| 267 | writeable ? "output" : "input", |
| 268 | strerror(errno)); |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 269 | return FILE_ERR_MMAP; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 270 | } |
| 271 | |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 272 | *buf = (uint8_t *)mmap_ptr; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 273 | *len = reasonable_len; |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 274 | return FILE_ERR_NONE; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 275 | } |
| 276 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 277 | enum futil_file_err futil_unmap_file(int fd, int writeable, |
| 278 | uint8_t *buf, uint32_t len) |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 279 | { |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 280 | void *mmap_ptr = buf; |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 281 | enum futil_file_err err = FILE_ERR_NONE; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 282 | |
| 283 | if (writeable && |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 284 | (0 != msync(mmap_ptr, len, MS_SYNC|MS_INVALIDATE))) { |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 285 | fprintf(stderr, "msync failed: %s\n", strerror(errno)); |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 286 | err = FILE_ERR_MSYNC; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 287 | } |
| 288 | |
Bill Richardson | e192e7f | 2014-09-23 12:49:26 -0700 | [diff] [blame] | 289 | if (0 != munmap(mmap_ptr, len)) { |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 290 | fprintf(stderr, "Can't munmap pointer: %s\n", |
| 291 | strerror(errno)); |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 292 | if (err == FILE_ERR_NONE) |
| 293 | err = FILE_ERR_MUNMAP; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 294 | } |
| 295 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 296 | return err; |
| 297 | } |
| 298 | |
| 299 | |
| 300 | #define DISK_SECTOR_SIZE 512 |
| 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; |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 325 | } |