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_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