1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 #include "avb_user_verification.h" 26 27 /* Maximum allow length (in bytes) of a partition name, including 28 * ab_suffix. 29 */ 30 #define AVB_PART_NAME_MAX_SIZE 32 31 32 /* Loads the toplevel AvbVBMetaImageHeader from the slot denoted by 33 * |ab_suffix| into |vbmeta_image|. No validation, verification, or 34 * byteswapping is performed. 35 * 36 * If successful, |true| is returned and the partition it was loaded 37 * from is returned in |out_partition_name| and the offset on said 38 * partition is returned in |out_vbmeta_offset|. 39 */ 40 static bool load_top_level_vbmeta_header( 41 AvbOps* ops, 42 const char* ab_suffix, 43 uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE], 44 char out_partition_name[AVB_PART_NAME_MAX_SIZE], 45 uint64_t* out_vbmeta_offset) { 46 uint64_t vbmeta_offset = 0; 47 size_t num_read; 48 bool ret = false; 49 AvbIOResult io_res; 50 51 /* Construct full partition name. */ 52 if (!avb_str_concat(out_partition_name, 53 AVB_PART_NAME_MAX_SIZE, 54 "vbmeta", 55 6, 56 ab_suffix, 57 avb_strlen(ab_suffix))) { 58 avb_error("Partition name and suffix does not fit.\n"); 59 goto out; 60 } 61 62 /* Only read the header, not the entire struct. */ 63 io_res = ops->read_from_partition(ops, 64 out_partition_name, 65 vbmeta_offset, 66 AVB_VBMETA_IMAGE_HEADER_SIZE, 67 vbmeta_image, 68 &num_read); 69 if (io_res == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) { 70 AvbFooter footer; 71 72 /* Try looking for the vbmeta struct in 'boot' via the footer. */ 73 if (!avb_str_concat(out_partition_name, 74 AVB_PART_NAME_MAX_SIZE, 75 "boot", 76 4, 77 ab_suffix, 78 avb_strlen(ab_suffix))) { 79 avb_error("Partition name and suffix does not fit.\n"); 80 goto out; 81 } 82 io_res = ops->read_from_partition(ops, 83 out_partition_name, 84 -AVB_FOOTER_SIZE, 85 AVB_FOOTER_SIZE, 86 &footer, 87 &num_read); 88 if (io_res != AVB_IO_RESULT_OK) { 89 avb_errorv("Error loading footer from partition '", 90 out_partition_name, 91 "'\n", 92 NULL); 93 goto out; 94 } 95 96 if (avb_memcmp(footer.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) { 97 avb_errorv("Data from '", 98 out_partition_name, 99 "' does not look like a vbmeta footer.\n", 100 NULL); 101 goto out; 102 } 103 104 vbmeta_offset = avb_be64toh(footer.vbmeta_offset); 105 io_res = ops->read_from_partition(ops, 106 out_partition_name, 107 vbmeta_offset, 108 AVB_VBMETA_IMAGE_HEADER_SIZE, 109 vbmeta_image, 110 &num_read); 111 } 112 113 if (io_res != AVB_IO_RESULT_OK) { 114 avb_errorv( 115 "Error loading from partition '", out_partition_name, "'\n", NULL); 116 goto out; 117 } 118 119 if (out_vbmeta_offset != NULL) { 120 *out_vbmeta_offset = vbmeta_offset; 121 } 122 123 ret = true; 124 125 out: 126 return ret; 127 } 128 129 bool avb_user_verification_get(AvbOps* ops, 130 const char* ab_suffix, 131 bool* out_verification_enabled) { 132 uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */ 133 char partition_name[AVB_PART_NAME_MAX_SIZE]; /* 32 bytes. */ 134 AvbVBMetaImageHeader* header; 135 uint32_t flags; 136 bool ret = false; 137 138 if (!load_top_level_vbmeta_header( 139 ops, ab_suffix, vbmeta_image, partition_name, NULL)) { 140 goto out; 141 } 142 143 if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) { 144 avb_errorv("Data from '", 145 partition_name, 146 "' does not look like a vbmeta header.\n", 147 NULL); 148 goto out; 149 } 150 151 /* Set/clear the VERIFICATION_DISABLED bit, as requested. */ 152 header = (AvbVBMetaImageHeader*)vbmeta_image; 153 flags = avb_be32toh(header->flags); 154 155 if (out_verification_enabled != NULL) { 156 *out_verification_enabled = 157 !(flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED); 158 } 159 160 ret = true; 161 162 out: 163 return ret; 164 } 165 166 bool avb_user_verification_set(AvbOps* ops, 167 const char* ab_suffix, 168 bool enable_verification) { 169 uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */ 170 char partition_name[AVB_PART_NAME_MAX_SIZE]; /* 32 bytes. */ 171 uint64_t vbmeta_offset; 172 AvbIOResult io_res; 173 AvbVBMetaImageHeader* header; 174 uint32_t flags; 175 bool ret = false; 176 177 if (!load_top_level_vbmeta_header( 178 ops, ab_suffix, vbmeta_image, partition_name, &vbmeta_offset)) { 179 goto out; 180 } 181 182 if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) { 183 avb_errorv("Data from '", 184 partition_name, 185 "' does not look like a vbmeta header.\n", 186 NULL); 187 goto out; 188 } 189 190 /* Set/clear the VERIFICATION_DISABLED bit, as requested. */ 191 header = (AvbVBMetaImageHeader*)vbmeta_image; 192 flags = avb_be32toh(header->flags); 193 flags &= ~AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED; 194 if (!enable_verification) { 195 flags |= AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED; 196 } 197 header->flags = avb_htobe32(flags); 198 199 /* Write the header. */ 200 io_res = ops->write_to_partition(ops, 201 partition_name, 202 vbmeta_offset, 203 AVB_VBMETA_IMAGE_HEADER_SIZE, 204 vbmeta_image); 205 if (io_res != AVB_IO_RESULT_OK) { 206 avb_errorv("Error writing to partition '", partition_name, "'\n", NULL); 207 goto out; 208 } 209 210 ret = true; 211 212 out: 213 return ret; 214 } 215