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_verity.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 */ 40static 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 125out: 126 return ret; 127} 128 129bool avb_user_verity_get(AvbOps* ops, 130 const char* ab_suffix, 131 bool* out_verity_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 HASHTREE_DISABLED bit, as requested. */ 152 header = (AvbVBMetaImageHeader*)vbmeta_image; 153 flags = avb_be32toh(header->flags); 154 155 if (out_verity_enabled != NULL) { 156 *out_verity_enabled = !(flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED); 157 } 158 159 ret = true; 160 161out: 162 return ret; 163} 164 165bool avb_user_verity_set(AvbOps* ops, 166 const char* ab_suffix, 167 bool enable_verity) { 168 uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */ 169 char partition_name[AVB_PART_NAME_MAX_SIZE]; /* 32 bytes. */ 170 uint64_t vbmeta_offset; 171 AvbIOResult io_res; 172 AvbVBMetaImageHeader* header; 173 uint32_t flags; 174 bool ret = false; 175 176 if (!load_top_level_vbmeta_header( 177 ops, ab_suffix, vbmeta_image, partition_name, &vbmeta_offset)) { 178 goto out; 179 } 180 181 if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) { 182 avb_errorv("Data from '", 183 partition_name, 184 "' does not look like a vbmeta header.\n", 185 NULL); 186 goto out; 187 } 188 189 /* Set/clear the HASHTREE_DISABLED bit, as requested. */ 190 header = (AvbVBMetaImageHeader*)vbmeta_image; 191 flags = avb_be32toh(header->flags); 192 flags &= ~AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED; 193 if (!enable_verity) { 194 flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED; 195 } 196 header->flags = avb_htobe32(flags); 197 198 /* Write the header. */ 199 io_res = ops->write_to_partition(ops, 200 partition_name, 201 vbmeta_offset, 202 AVB_VBMETA_IMAGE_HEADER_SIZE, 203 vbmeta_image); 204 if (io_res != AVB_IO_RESULT_OK) { 205 avb_errorv("Error writing to partition '", partition_name, "'\n", NULL); 206 goto out; 207 } 208 209 ret = true; 210 211out: 212 return ret; 213} 214