1 /* Copyright 2015 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 * 5 * This utility wraps around "cgpt" execution to work with NAND. If the target 6 * device is an MTD device, this utility will read the GPT structures from 7 * FMAP, invokes "cgpt" on that, and writes the result back to NOR flash. */ 8 9 #include <err.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <inttypes.h> 13 #include <limits.h> 14 #include <linux/major.h> 15 #include <stdbool.h> 16 #include <stdlib.h> 17 #include <stdint.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 24 #include "cgpt.h" 25 #include "cgpt_nor.h" 26 #include "cryptolib.h" 27 28 // Check if cmdline |argv| has "-D". "-D" signifies that GPT structs are stored 29 // off device, and hence we should not wrap around cgpt. 30 static bool has_dash_D(int argc, const char *const argv[]) { 31 int i; 32 // We go from 2, because the second arg is a cgpt command such as "create". 33 for (i = 2; i < argc; ++i) { 34 if (strcmp("-D", argv[i]) == 0) { 35 return true; 36 } 37 } 38 return false; 39 } 40 41 // Check if |device_path| is an MTD device based on its major number being 90. 42 static bool is_mtd(const char *device_path) { 43 struct stat stat; 44 if (lstat(device_path, &stat) != 0) { 45 return false; 46 } 47 48 if (major(stat.st_rdev) != MTD_CHAR_MAJOR) { 49 return false; 50 } 51 52 return true; 53 } 54 55 // Return the element in |argv| that is an MTD device. 56 static const char *find_mtd_device(int argc, const char *const argv[]) { 57 int i; 58 for (i = 2; i < argc; ++i) { 59 if (is_mtd(argv[i])) { 60 return argv[i]; 61 } 62 } 63 return NULL; 64 } 65 66 static int wrap_cgpt(int argc, 67 const char *const argv[], 68 const char *mtd_device) { 69 uint8_t *original_hash = NULL; 70 uint8_t *modified_hash = NULL; 71 int ret = 0; 72 73 // Create a temp dir to work in. 74 ret++; 75 char temp_dir[] = "/tmp/cgpt_wrapper.XXXXXX"; 76 if (ReadNorFlash(temp_dir) != 0) { 77 return ret; 78 } 79 char rw_gpt_path[PATH_MAX]; 80 if (snprintf(rw_gpt_path, sizeof(rw_gpt_path), "%s/rw_gpt", temp_dir) < 0) { 81 goto cleanup; 82 } 83 original_hash = DigestFile(rw_gpt_path, SHA1_DIGEST_ALGORITHM); 84 85 // Obtain the MTD size. 86 ret++; 87 uint64_t drive_size = 0; 88 if (GetMtdSize(mtd_device, &drive_size) != 0) { 89 Error("Cannot get the size of %s.\n", mtd_device); 90 goto cleanup; 91 } 92 93 // Launch cgpt on "rw_gpt" with -D size. 94 ret++; 95 const char** my_argv = calloc(argc + 2 + 1, sizeof(char *)); 96 if (my_argv == NULL) { 97 errno = ENOMEM; 98 goto cleanup; 99 } 100 memcpy(my_argv, argv, sizeof(char *) * argc); 101 char *real_cgpt; 102 if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) { 103 free(my_argv); 104 goto cleanup; 105 } 106 my_argv[0] = real_cgpt; 107 108 int i; 109 for (i = 2; i < argc; ++i) { 110 if (strcmp(my_argv[i], mtd_device) == 0) { 111 my_argv[i] = rw_gpt_path; 112 } 113 } 114 my_argv[argc] = "-D"; 115 char size[32]; 116 snprintf(size, sizeof(size), "%" PRIu64, drive_size); 117 my_argv[argc + 1] = size; 118 i = ForkExecV(NULL, my_argv); 119 free(real_cgpt); 120 free(my_argv); 121 if (i != 0) { 122 Error("Cannot exec cgpt to modify rw_gpt.\n"); 123 goto cleanup; 124 } 125 126 // Write back "rw_gpt" to NOR flash in two chunks. 127 ret++; 128 modified_hash = DigestFile(rw_gpt_path, SHA1_DIGEST_ALGORITHM); 129 if (original_hash != NULL && modified_hash != NULL) { 130 if (memcmp(original_hash, modified_hash, SHA1_DIGEST_SIZE) != 0) { 131 ret = WriteNorFlash(temp_dir); 132 } else { 133 ret = 0; 134 } 135 } 136 137 cleanup: 138 free(original_hash); 139 free(modified_hash); 140 RemoveDir(temp_dir); 141 return ret; 142 } 143 144 int main(int argc, const char *argv[]) { 145 char resolved_cgpt[PATH_MAX]; 146 pid_t pid = getpid(); 147 char exe_link[40]; 148 149 if (argc < 1) { 150 return -1; 151 } 152 153 snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid); 154 memset(resolved_cgpt, 0, sizeof(resolved_cgpt)); 155 if (readlink(exe_link, resolved_cgpt, sizeof(resolved_cgpt) - 1) == -1) { 156 perror("readlink"); 157 return -1; 158 } 159 160 argv[0] = resolved_cgpt; 161 162 if (argc > 2 && !has_dash_D(argc, argv)) { 163 const char *mtd_device = find_mtd_device(argc, argv); 164 if (mtd_device) { 165 return wrap_cgpt(argc, argv, mtd_device); 166 } 167 } 168 169 // Forward to cgpt as-is. Real cgpt has been renamed cgpt.bin. 170 char *real_cgpt; 171 if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) { 172 return -1; 173 } 174 argv[0] = real_cgpt; 175 if (execv(argv[0], (char * const *)argv) == -1) { 176 err(-2, "execv(%s) failed", real_cgpt); 177 } 178 return -2; 179 } 180