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