uboot/lib/libavb/avb_cmdline.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright (C) 2016 The Android Open Source Project
   4 */
   5
   6#include "avb_cmdline.h"
   7#include "avb_sha.h"
   8#include "avb_util.h"
   9#include "avb_version.h"
  10#include <log.h>
  11#include <malloc.h>
  12
  13#define NUM_GUIDS 3
  14
  15/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
  16 * values. Returns NULL on OOM, otherwise the cmdline with values
  17 * replaced.
  18 */
  19char* avb_sub_cmdline(AvbOps* ops,
  20                      const char* cmdline,
  21                      const char* ab_suffix,
  22                      bool using_boot_for_vbmeta,
  23                      const AvbCmdlineSubstList* additional_substitutions) {
  24  const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
  25  const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
  26                                        "$(ANDROID_BOOT_PARTUUID)",
  27                                        "$(ANDROID_VBMETA_PARTUUID)"};
  28  char* ret = NULL;
  29  AvbIOResult io_ret;
  30  size_t n;
  31
  32  /* Special-case for when the top-level vbmeta struct is in the boot
  33   * partition.
  34   */
  35  if (using_boot_for_vbmeta) {
  36    part_name_str[2] = "boot";
  37  }
  38
  39  /* Replace unique partition GUIDs */
  40  for (n = 0; n < NUM_GUIDS; n++) {
  41    char part_name[AVB_PART_NAME_MAX_SIZE];
  42    char guid_buf[37];
  43
  44    /* Don't attempt to query the partition guid unless its search string is
  45     * present in the command line. Note: the original cmdline is used here,
  46     * not the replaced one. See b/116010959.
  47     */
  48    if (avb_strstr(cmdline, replace_str[n]) == NULL) {
  49      continue;
  50    }
  51
  52    if (!avb_str_concat(part_name,
  53                        sizeof part_name,
  54                        part_name_str[n],
  55                        avb_strlen(part_name_str[n]),
  56                        ab_suffix,
  57                        avb_strlen(ab_suffix))) {
  58      avb_error("Partition name and suffix does not fit.\n");
  59      goto fail;
  60    }
  61
  62    io_ret = ops->get_unique_guid_for_partition(
  63        ops, part_name, guid_buf, sizeof guid_buf);
  64    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
  65      goto fail;
  66    } else if (io_ret != AVB_IO_RESULT_OK) {
  67      avb_error("Error getting unique GUID for partition.\n");
  68      goto fail;
  69    }
  70
  71    if (ret == NULL) {
  72      ret = avb_replace(cmdline, replace_str[n], guid_buf);
  73    } else {
  74      char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
  75      avb_free(ret);
  76      ret = new_ret;
  77    }
  78    if (ret == NULL) {
  79      goto fail;
  80    }
  81  }
  82
  83  /* It's possible there is no _PARTUUID for replacement above.
  84   * Duplicate cmdline to ret for additional substitutions below.
  85   */
  86  if (ret == NULL) {
  87    ret = avb_strdup(cmdline);
  88    if (ret == NULL) {
  89      goto fail;
  90    }
  91  }
  92
  93  /* Replace any additional substitutions. */
  94  if (additional_substitutions != NULL) {
  95    for (n = 0; n < additional_substitutions->size; ++n) {
  96      char* new_ret = avb_replace(ret,
  97                                  additional_substitutions->tokens[n],
  98                                  additional_substitutions->values[n]);
  99      avb_free(ret);
 100      ret = new_ret;
 101      if (ret == NULL) {
 102        goto fail;
 103      }
 104    }
 105  }
 106
 107  return ret;
 108
 109fail:
 110  if (ret != NULL) {
 111    avb_free(ret);
 112  }
 113  return NULL;
 114}
 115
 116static int cmdline_append_option(AvbSlotVerifyData* slot_data,
 117                                 const char* key,
 118                                 const char* value) {
 119  size_t offset, key_len, value_len;
 120  char* new_cmdline;
 121
 122  key_len = avb_strlen(key);
 123  value_len = avb_strlen(value);
 124
 125  offset = 0;
 126  if (slot_data->cmdline != NULL) {
 127    offset = avb_strlen(slot_data->cmdline);
 128    if (offset > 0) {
 129      offset += 1;
 130    }
 131  }
 132
 133  new_cmdline = avb_calloc(offset + key_len + value_len + 2);
 134  if (new_cmdline == NULL) {
 135    return 0;
 136  }
 137  if (offset > 0) {
 138    avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
 139    new_cmdline[offset - 1] = ' ';
 140  }
 141  avb_memcpy(new_cmdline + offset, key, key_len);
 142  new_cmdline[offset + key_len] = '=';
 143  avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
 144  if (slot_data->cmdline != NULL) {
 145    avb_free(slot_data->cmdline);
 146  }
 147  slot_data->cmdline = new_cmdline;
 148
 149  return 1;
 150}
 151
 152#define AVB_MAX_DIGITS_UINT64 32
 153
 154/* Writes |value| to |digits| in base 10 followed by a NUL byte.
 155 * Returns number of characters written excluding the NUL byte.
 156 */
 157static size_t uint64_to_base10(uint64_t value,
 158                               char digits[AVB_MAX_DIGITS_UINT64]) {
 159  char rev_digits[AVB_MAX_DIGITS_UINT64];
 160  size_t n, num_digits;
 161
 162  for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
 163    rev_digits[num_digits++] = avb_div_by_10(&value) + '0';
 164    if (value == 0) {
 165      break;
 166    }
 167  }
 168
 169  for (n = 0; n < num_digits; n++) {
 170    digits[n] = rev_digits[num_digits - 1 - n];
 171  }
 172  digits[n] = '\0';
 173  return n;
 174}
 175
 176static int cmdline_append_version(AvbSlotVerifyData* slot_data,
 177                                  const char* key,
 178                                  uint64_t major_version,
 179                                  uint64_t minor_version) {
 180  char major_digits[AVB_MAX_DIGITS_UINT64];
 181  char minor_digits[AVB_MAX_DIGITS_UINT64];
 182  char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
 183  size_t num_major_digits, num_minor_digits;
 184
 185  num_major_digits = uint64_to_base10(major_version, major_digits);
 186  num_minor_digits = uint64_to_base10(minor_version, minor_digits);
 187  avb_memcpy(combined, major_digits, num_major_digits);
 188  combined[num_major_digits] = '.';
 189  avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
 190  combined[num_major_digits + 1 + num_minor_digits] = '\0';
 191
 192  return cmdline_append_option(slot_data, key, combined);
 193}
 194
 195static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
 196                                        const char* key,
 197                                        uint64_t value) {
 198  char digits[AVB_MAX_DIGITS_UINT64];
 199  uint64_to_base10(value, digits);
 200  return cmdline_append_option(slot_data, key, digits);
 201}
 202
 203static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
 204                              const char* key,
 205                              const uint8_t* data,
 206                              size_t data_len) {
 207  int ret;
 208  char* hex_data = avb_bin2hex(data, data_len);
 209  if (hex_data == NULL) {
 210    return 0;
 211  }
 212  ret = cmdline_append_option(slot_data, key, hex_data);
 213  avb_free(hex_data);
 214  return ret;
 215}
 216
 217AvbSlotVerifyResult avb_append_options(
 218    AvbOps* ops,
 219    AvbSlotVerifyFlags flags,
 220    AvbSlotVerifyData* slot_data,
 221    AvbVBMetaImageHeader* toplevel_vbmeta,
 222    AvbAlgorithmType algorithm_type,
 223    AvbHashtreeErrorMode hashtree_error_mode,
 224    AvbHashtreeErrorMode resolved_hashtree_error_mode) {
 225  AvbSlotVerifyResult ret;
 226  const char* verity_mode;
 227  bool is_device_unlocked;
 228  AvbIOResult io_ret;
 229
 230  /* Add androidboot.vbmeta.device option... except if not using a vbmeta
 231   * partition since it doesn't make sense in that case.
 232   */
 233  if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
 234    if (!cmdline_append_option(slot_data,
 235                               "androidboot.vbmeta.device",
 236                               "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
 237      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 238      goto out;
 239    }
 240  }
 241
 242  /* Add androidboot.vbmeta.avb_version option. */
 243  if (!cmdline_append_version(slot_data,
 244                              "androidboot.vbmeta.avb_version",
 245                              AVB_VERSION_MAJOR,
 246                              AVB_VERSION_MINOR)) {
 247    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 248    goto out;
 249  }
 250
 251  /* Set androidboot.avb.device_state to "locked" or "unlocked". */
 252  io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
 253  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
 254    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 255    goto out;
 256  } else if (io_ret != AVB_IO_RESULT_OK) {
 257    avb_error("Error getting device state.\n");
 258    ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
 259    goto out;
 260  }
 261  if (!cmdline_append_option(slot_data,
 262                             "androidboot.vbmeta.device_state",
 263                             is_device_unlocked ? "unlocked" : "locked")) {
 264    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 265    goto out;
 266  }
 267
 268  /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
 269   * function as is used to sign vbmeta.
 270   */
 271  switch (algorithm_type) {
 272    /* Explicit fallthrough. */
 273    case AVB_ALGORITHM_TYPE_NONE:
 274    case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
 275    case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
 276    case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
 277      size_t n, total_size = 0;
 278      uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
 279      avb_slot_verify_data_calculate_vbmeta_digest(
 280          slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
 281      for (n = 0; n < slot_data->num_vbmeta_images; n++) {
 282        total_size += slot_data->vbmeta_images[n].vbmeta_size;
 283      }
 284      if (!cmdline_append_option(
 285              slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
 286          !cmdline_append_uint64_base10(
 287              slot_data, "androidboot.vbmeta.size", total_size) ||
 288          !cmdline_append_hex(slot_data,
 289                              "androidboot.vbmeta.digest",
 290                              vbmeta_digest,
 291                              AVB_SHA256_DIGEST_SIZE)) {
 292        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 293        goto out;
 294      }
 295    } break;
 296    /* Explicit fallthrough. */
 297    case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
 298    case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
 299    case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
 300      size_t n, total_size = 0;
 301      uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE];
 302      avb_slot_verify_data_calculate_vbmeta_digest(
 303          slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest);
 304      for (n = 0; n < slot_data->num_vbmeta_images; n++) {
 305        total_size += slot_data->vbmeta_images[n].vbmeta_size;
 306      }
 307      if (!cmdline_append_option(
 308              slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
 309          !cmdline_append_uint64_base10(
 310              slot_data, "androidboot.vbmeta.size", total_size) ||
 311          !cmdline_append_hex(slot_data,
 312                              "androidboot.vbmeta.digest",
 313                              vbmeta_digest,
 314                              AVB_SHA512_DIGEST_SIZE)) {
 315        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 316        goto out;
 317      }
 318    } break;
 319    case _AVB_ALGORITHM_NUM_TYPES:
 320      avb_assert_not_reached();
 321      break;
 322  }
 323
 324  /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */
 325  if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
 326    verity_mode = "disabled";
 327  } else {
 328    const char* dm_verity_mode;
 329    char* new_ret;
 330
 331    switch (resolved_hashtree_error_mode) {
 332      case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
 333        if (!cmdline_append_option(
 334                slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
 335          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 336          goto out;
 337        }
 338        verity_mode = "enforcing";
 339        dm_verity_mode = "restart_on_corruption";
 340        break;
 341      case AVB_HASHTREE_ERROR_MODE_RESTART:
 342        verity_mode = "enforcing";
 343        dm_verity_mode = "restart_on_corruption";
 344        break;
 345      case AVB_HASHTREE_ERROR_MODE_EIO:
 346        verity_mode = "eio";
 347        /* For now there's no option to specify the EIO mode. So
 348         * just use 'ignore_zero_blocks' since that's already set
 349         * and dm-verity-target.c supports specifying this multiple
 350         * times.
 351         */
 352        dm_verity_mode = "ignore_zero_blocks";
 353        break;
 354      case AVB_HASHTREE_ERROR_MODE_LOGGING:
 355        verity_mode = "logging";
 356        dm_verity_mode = "ignore_corruption";
 357        break;
 358      case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO:
 359        // Should never get here because MANAGED_RESTART_AND_EIO is
 360        // remapped by avb_manage_hashtree_error_mode().
 361        avb_assert_not_reached();
 362        ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
 363        goto out;
 364      default:
 365        ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
 366        goto out;
 367    }
 368    new_ret = avb_replace(
 369        slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
 370    avb_free(slot_data->cmdline);
 371    slot_data->cmdline = new_ret;
 372    if (slot_data->cmdline == NULL) {
 373      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 374      goto out;
 375    }
 376  }
 377  if (!cmdline_append_option(
 378          slot_data, "androidboot.veritymode", verity_mode)) {
 379    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 380    goto out;
 381  }
 382  if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
 383    if (!cmdline_append_option(
 384            slot_data, "androidboot.veritymode.managed", "yes")) {
 385      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 386      goto out;
 387    }
 388  }
 389
 390  ret = AVB_SLOT_VERIFY_RESULT_OK;
 391
 392out:
 393
 394  return ret;
 395}
 396
 397AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
 398  return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
 399}
 400
 401void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
 402  size_t i;
 403  for (i = 0; i < cmdline_subst->size; ++i) {
 404    avb_free(cmdline_subst->tokens[i]);
 405    avb_free(cmdline_subst->values[i]);
 406  }
 407  cmdline_subst->size = 0;
 408  avb_free(cmdline_subst);
 409}
 410
 411AvbSlotVerifyResult avb_add_root_digest_substitution(
 412    const char* part_name,
 413    const uint8_t* digest,
 414    size_t digest_size,
 415    AvbCmdlineSubstList* out_cmdline_subst) {
 416  const char* kDigestSubPrefix = "$(AVB_";
 417  const char* kDigestSubSuffix = "_ROOT_DIGEST)";
 418  size_t part_name_len = avb_strlen(part_name);
 419  size_t list_index = out_cmdline_subst->size;
 420
 421  avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE);
 422  avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE);
 423  if (part_name_len >= AVB_PART_NAME_MAX_SIZE ||
 424      digest_size > AVB_SHA512_DIGEST_SIZE) {
 425    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
 426  }
 427
 428  if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) {
 429    /* The list is full. Currently dynamic growth of this list is not supported.
 430     */
 431    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
 432  }
 433
 434  /* Construct the token to replace in the command line based on the partition
 435   * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'.
 436   */
 437  out_cmdline_subst->tokens[list_index] =
 438      avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
 439  if (out_cmdline_subst->tokens[list_index] == NULL) {
 440    goto fail;
 441  }
 442  avb_uppercase(out_cmdline_subst->tokens[list_index]);
 443
 444  /* The digest value is hex encoded when inserted in the command line. */
 445  out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size);
 446  if (out_cmdline_subst->values[list_index] == NULL) {
 447    goto fail;
 448  }
 449
 450  out_cmdline_subst->size++;
 451  return AVB_SLOT_VERIFY_RESULT_OK;
 452
 453fail:
 454  if (out_cmdline_subst->tokens[list_index]) {
 455    avb_free(out_cmdline_subst->tokens[list_index]);
 456  }
 457  if (out_cmdline_subst->values[list_index]) {
 458    avb_free(out_cmdline_subst->values[list_index]);
 459  }
 460  return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
 461}
 462