Home | History | Annotate | Download | only in libavb_user
      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  */
     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_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 
    161 out:
    162   return ret;
    163 }
    164 
    165 bool 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 
    211 out:
    212   return ret;
    213 }
    214