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