1 // SPDX-License-Identifier: MIT
3 * Copyright (C) 2016 The Android Open Source Project
6 #include "avb_cmdline.h"
9 #include "avb_version.h"
14 /* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
15 * values. Returns NULL on OOM, otherwise the cmdline with values
18 char* avb_sub_cmdline(AvbOps* ops,
20 const char* ab_suffix,
21 bool using_boot_for_vbmeta,
22 const AvbCmdlineSubstList* additional_substitutions) {
23 const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
24 const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
25 "$(ANDROID_BOOT_PARTUUID)",
26 "$(ANDROID_VBMETA_PARTUUID)"};
31 /* Special-case for when the top-level vbmeta struct is in the boot
34 if (using_boot_for_vbmeta) {
35 part_name_str[2] = "boot";
38 /* Replace unique partition GUIDs */
39 for (n = 0; n < NUM_GUIDS; n++) {
40 char part_name[AVB_PART_NAME_MAX_SIZE];
43 /* Don't attempt to query the partition guid unless its search string is
44 * present in the command line. Note: the original cmdline is used here,
45 * not the replaced one. See b/116010959.
47 if (avb_strstr(cmdline, replace_str[n]) == NULL) {
51 if (!avb_str_concat(part_name,
54 avb_strlen(part_name_str[n]),
56 avb_strlen(ab_suffix))) {
57 avb_error("Partition name and suffix does not fit.\n");
61 io_ret = ops->get_unique_guid_for_partition(
62 ops, part_name, guid_buf, sizeof guid_buf);
63 if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
65 } else if (io_ret != AVB_IO_RESULT_OK) {
66 avb_error("Error getting unique GUID for partition.\n");
71 ret = avb_replace(cmdline, replace_str[n], guid_buf);
73 char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
82 /* It's possible there is no _PARTUUID for replacement above.
83 * Duplicate cmdline to ret for additional substitutions below.
86 ret = avb_strdup(cmdline);
92 /* Replace any additional substitutions. */
93 if (additional_substitutions != NULL) {
94 for (n = 0; n < additional_substitutions->size; ++n) {
95 char* new_ret = avb_replace(ret,
96 additional_substitutions->tokens[n],
97 additional_substitutions->values[n]);
115 static int cmdline_append_option(AvbSlotVerifyData* slot_data,
118 size_t offset, key_len, value_len;
121 key_len = avb_strlen(key);
122 value_len = avb_strlen(value);
125 if (slot_data->cmdline != NULL) {
126 offset = avb_strlen(slot_data->cmdline);
132 new_cmdline = avb_calloc(offset + key_len + value_len + 2);
133 if (new_cmdline == NULL) {
137 avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
138 new_cmdline[offset - 1] = ' ';
140 avb_memcpy(new_cmdline + offset, key, key_len);
141 new_cmdline[offset + key_len] = '=';
142 avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
143 if (slot_data->cmdline != NULL) {
144 avb_free(slot_data->cmdline);
146 slot_data->cmdline = new_cmdline;
151 #define AVB_MAX_DIGITS_UINT64 32
153 /* Writes |value| to |digits| in base 10 followed by a NUL byte.
154 * Returns number of characters written excluding the NUL byte.
156 static size_t uint64_to_base10(uint64_t value,
157 char digits[AVB_MAX_DIGITS_UINT64]) {
158 char rev_digits[AVB_MAX_DIGITS_UINT64];
159 size_t n, num_digits;
161 for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
162 rev_digits[num_digits++] = avb_div_by_10(&value) + '0';
168 for (n = 0; n < num_digits; n++) {
169 digits[n] = rev_digits[num_digits - 1 - n];
175 static int cmdline_append_version(AvbSlotVerifyData* slot_data,
177 uint64_t major_version,
178 uint64_t minor_version) {
179 char major_digits[AVB_MAX_DIGITS_UINT64];
180 char minor_digits[AVB_MAX_DIGITS_UINT64];
181 char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
182 size_t num_major_digits, num_minor_digits;
184 num_major_digits = uint64_to_base10(major_version, major_digits);
185 num_minor_digits = uint64_to_base10(minor_version, minor_digits);
186 avb_memcpy(combined, major_digits, num_major_digits);
187 combined[num_major_digits] = '.';
188 avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
189 combined[num_major_digits + 1 + num_minor_digits] = '\0';
191 return cmdline_append_option(slot_data, key, combined);
194 static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
197 char digits[AVB_MAX_DIGITS_UINT64];
198 uint64_to_base10(value, digits);
199 return cmdline_append_option(slot_data, key, digits);
202 static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
207 char* hex_data = avb_bin2hex(data, data_len);
208 if (hex_data == NULL) {
211 ret = cmdline_append_option(slot_data, key, hex_data);
216 AvbSlotVerifyResult avb_append_options(
218 AvbSlotVerifyFlags flags,
219 AvbSlotVerifyData* slot_data,
220 AvbVBMetaImageHeader* toplevel_vbmeta,
221 AvbAlgorithmType algorithm_type,
222 AvbHashtreeErrorMode hashtree_error_mode,
223 AvbHashtreeErrorMode resolved_hashtree_error_mode) {
224 AvbSlotVerifyResult ret;
225 const char* verity_mode;
226 bool is_device_unlocked;
229 /* Add androidboot.vbmeta.device option... except if not using a vbmeta
230 * partition since it doesn't make sense in that case.
232 if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
233 if (!cmdline_append_option(slot_data,
234 "androidboot.vbmeta.device",
235 "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
236 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
241 /* Add androidboot.vbmeta.avb_version option. */
242 if (!cmdline_append_version(slot_data,
243 "androidboot.vbmeta.avb_version",
245 AVB_VERSION_MINOR)) {
246 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
250 /* Set androidboot.avb.device_state to "locked" or "unlocked". */
251 io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
252 if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
253 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
255 } else if (io_ret != AVB_IO_RESULT_OK) {
256 avb_error("Error getting device state.\n");
257 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
260 if (!cmdline_append_option(slot_data,
261 "androidboot.vbmeta.device_state",
262 is_device_unlocked ? "unlocked" : "locked")) {
263 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
267 /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
268 * function as is used to sign vbmeta.
270 switch (algorithm_type) {
271 /* Explicit fallthrough. */
272 case AVB_ALGORITHM_TYPE_NONE:
273 case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
274 case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
275 case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
276 size_t n, total_size = 0;
277 uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
278 avb_slot_verify_data_calculate_vbmeta_digest(
279 slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
280 for (n = 0; n < slot_data->num_vbmeta_images; n++) {
281 total_size += slot_data->vbmeta_images[n].vbmeta_size;
283 if (!cmdline_append_option(
284 slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
285 !cmdline_append_uint64_base10(
286 slot_data, "androidboot.vbmeta.size", total_size) ||
287 !cmdline_append_hex(slot_data,
288 "androidboot.vbmeta.digest",
290 AVB_SHA256_DIGEST_SIZE)) {
291 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
295 /* Explicit fallthrough. */
296 case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
297 case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
298 case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
299 size_t n, total_size = 0;
300 uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE];
301 avb_slot_verify_data_calculate_vbmeta_digest(
302 slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest);
303 for (n = 0; n < slot_data->num_vbmeta_images; n++) {
304 total_size += slot_data->vbmeta_images[n].vbmeta_size;
306 if (!cmdline_append_option(
307 slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
308 !cmdline_append_uint64_base10(
309 slot_data, "androidboot.vbmeta.size", total_size) ||
310 !cmdline_append_hex(slot_data,
311 "androidboot.vbmeta.digest",
313 AVB_SHA512_DIGEST_SIZE)) {
314 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
318 case _AVB_ALGORITHM_NUM_TYPES:
319 avb_assert_not_reached();
323 /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */
324 if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
325 verity_mode = "disabled";
327 const char* dm_verity_mode;
330 switch (resolved_hashtree_error_mode) {
331 case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
332 if (!cmdline_append_option(
333 slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
334 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
337 verity_mode = "enforcing";
338 dm_verity_mode = "restart_on_corruption";
340 case AVB_HASHTREE_ERROR_MODE_RESTART:
341 verity_mode = "enforcing";
342 dm_verity_mode = "restart_on_corruption";
344 case AVB_HASHTREE_ERROR_MODE_EIO:
346 /* For now there's no option to specify the EIO mode. So
347 * just use 'ignore_zero_blocks' since that's already set
348 * and dm-verity-target.c supports specifying this multiple
351 dm_verity_mode = "ignore_zero_blocks";
353 case AVB_HASHTREE_ERROR_MODE_LOGGING:
354 verity_mode = "logging";
355 dm_verity_mode = "ignore_corruption";
357 case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO:
358 // Should never get here because MANAGED_RESTART_AND_EIO is
359 // remapped by avb_manage_hashtree_error_mode().
360 avb_assert_not_reached();
361 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
364 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
367 new_ret = avb_replace(
368 slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
369 avb_free(slot_data->cmdline);
370 slot_data->cmdline = new_ret;
371 if (slot_data->cmdline == NULL) {
372 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
376 if (!cmdline_append_option(
377 slot_data, "androidboot.veritymode", verity_mode)) {
378 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
381 if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
382 if (!cmdline_append_option(
383 slot_data, "androidboot.veritymode.managed", "yes")) {
384 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
389 ret = AVB_SLOT_VERIFY_RESULT_OK;
396 AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
397 return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
400 void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
402 for (i = 0; i < cmdline_subst->size; ++i) {
403 avb_free(cmdline_subst->tokens[i]);
404 avb_free(cmdline_subst->values[i]);
406 cmdline_subst->size = 0;
407 avb_free(cmdline_subst);
410 AvbSlotVerifyResult avb_add_root_digest_substitution(
411 const char* part_name,
412 const uint8_t* digest,
414 AvbCmdlineSubstList* out_cmdline_subst) {
415 const char* kDigestSubPrefix = "$(AVB_";
416 const char* kDigestSubSuffix = "_ROOT_DIGEST)";
417 size_t part_name_len = avb_strlen(part_name);
418 size_t list_index = out_cmdline_subst->size;
420 avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE);
421 avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE);
422 if (part_name_len >= AVB_PART_NAME_MAX_SIZE ||
423 digest_size > AVB_SHA512_DIGEST_SIZE) {
424 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
427 if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) {
428 /* The list is full. Currently dynamic growth of this list is not supported.
430 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
433 /* Construct the token to replace in the command line based on the partition
434 * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'.
436 out_cmdline_subst->tokens[list_index] =
437 avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
438 if (out_cmdline_subst->tokens[list_index] == NULL) {
441 avb_uppercase(out_cmdline_subst->tokens[list_index]);
443 /* The digest value is hex encoded when inserted in the command line. */
444 out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size);
445 if (out_cmdline_subst->values[list_index] == NULL) {
449 out_cmdline_subst->size++;
450 return AVB_SLOT_VERIFY_RESULT_OK;
453 if (out_cmdline_subst->tokens[list_index]) {
454 avb_free(out_cmdline_subst->tokens[list_index]);
456 if (out_cmdline_subst->values[list_index]) {
457 avb_free(out_cmdline_subst->values[list_index]);
459 return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;