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
cmdline_append_version(AvbSlotVerifyData * slot_data,const char * key,uint64_t major_version,uint64_t minor_version)169 static int cmdline_append_version(AvbSlotVerifyData* slot_data,
170 const char* key,
171 uint64_t major_version,
172 uint64_t minor_version) {
173 char major_digits[AVB_MAX_DIGITS_UINT64];
174 char minor_digits[AVB_MAX_DIGITS_UINT64];
175 char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
176 size_t num_major_digits, num_minor_digits;
177
178 num_major_digits = avb_uint64_to_base10(major_version, major_digits);
179 num_minor_digits = avb_uint64_to_base10(minor_version, minor_digits);
180 avb_memcpy(combined, major_digits, num_major_digits);
181 combined[num_major_digits] = '.';
182 avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
183 combined[num_major_digits + 1 + num_minor_digits] = '\0';
184
185 return cmdline_append_option(slot_data, key, combined);
186 }
187
cmdline_append_uint64_base10(AvbSlotVerifyData * slot_data,const char * key,uint64_t value)188 static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
189 const char* key,
190 uint64_t value) {
191 char digits[AVB_MAX_DIGITS_UINT64];
192 avb_uint64_to_base10(value, digits);
193 return cmdline_append_option(slot_data, key, digits);
194 }
195
cmdline_append_hex(AvbSlotVerifyData * slot_data,const char * key,const uint8_t * data,size_t data_len)196 static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
197 const char* key,
198 const uint8_t* data,
199 size_t data_len) {
200 int ret;
201 char* hex_data = avb_bin2hex(data, data_len);
202 if (hex_data == NULL) {
203 return 0;
204 }
205 ret = cmdline_append_option(slot_data, key, hex_data);
206 avb_free(hex_data);
207 return ret;
208 }
209
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)210 AvbSlotVerifyResult avb_append_options(
211 AvbOps* ops,
212 AvbSlotVerifyFlags flags,
213 AvbSlotVerifyData* slot_data,
214 AvbVBMetaImageHeader* toplevel_vbmeta,
215 AvbAlgorithmType algorithm_type,
216 AvbHashtreeErrorMode hashtree_error_mode,
217 AvbHashtreeErrorMode resolved_hashtree_error_mode) {
218 AvbSlotVerifyResult ret;
219 const char* verity_mode;
220 bool is_device_unlocked;
221 AvbIOResult io_ret;
222
223 /* Add androidboot.vbmeta.device option... except if not using a vbmeta
224 * partition since it doesn't make sense in that case.
225 */
226 if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
227 if (!cmdline_append_option(slot_data,
228 "androidboot.vbmeta.device",
229 "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
230 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
231 goto out;
232 }
233 }
234
235 /* Add androidboot.vbmeta.avb_version option. */
236 if (!cmdline_append_version(slot_data,
237 "androidboot.vbmeta.avb_version",
238 AVB_VERSION_MAJOR,
239 AVB_VERSION_MINOR)) {
240 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
241 goto out;
242 }
243
244 /* Set androidboot.avb.device_state to "locked" or "unlocked". */
245 io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
246 if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
247 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
248 goto out;
249 } else if (io_ret != AVB_IO_RESULT_OK) {
250 avb_error("Error getting device state.\n");
251 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
252 goto out;
253 }
254 if (!cmdline_append_option(slot_data,
255 "androidboot.vbmeta.device_state",
256 is_device_unlocked ? "unlocked" : "locked")) {
257 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
258 goto out;
259 }
260
261 /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
262 * function as is used to sign vbmeta.
263 */
264 switch (algorithm_type) {
265 /* Explicit fallthrough. */
266 case AVB_ALGORITHM_TYPE_NONE:
267 case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
268 case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
269 case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
270 size_t n, total_size = 0;
271 uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
272 avb_slot_verify_data_calculate_vbmeta_digest(
273 slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
274 for (n = 0; n < slot_data->num_vbmeta_images; n++) {
275 total_size += slot_data->vbmeta_images[n].vbmeta_size;
276 }
277 if (!cmdline_append_option(
278 slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
279 !cmdline_append_uint64_base10(
280 slot_data, "androidboot.vbmeta.size", total_size) ||
281 !cmdline_append_hex(slot_data,
282 "androidboot.vbmeta.digest",
283 vbmeta_digest,
284 AVB_SHA256_DIGEST_SIZE)) {
285 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
286 goto out;
287 }
288 } break;
289 /* Explicit fallthrough. */
290 case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
291 case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
292 case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
293 size_t n, total_size = 0;
294 uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE];
295 avb_slot_verify_data_calculate_vbmeta_digest(
296 slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest);
297 for (n = 0; n < slot_data->num_vbmeta_images; n++) {
298 total_size += slot_data->vbmeta_images[n].vbmeta_size;
299 }
300 if (!cmdline_append_option(
301 slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
302 !cmdline_append_uint64_base10(
303 slot_data, "androidboot.vbmeta.size", total_size) ||
304 !cmdline_append_hex(slot_data,
305 "androidboot.vbmeta.digest",
306 vbmeta_digest,
307 AVB_SHA512_DIGEST_SIZE)) {
308 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
309 goto out;
310 }
311 } break;
312 case _AVB_ALGORITHM_NUM_TYPES:
313 avb_assert_not_reached();
314 break;
315 }
316
317 /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */
318 if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
319 verity_mode = "disabled";
320 } else {
321 const char* dm_verity_mode;
322 char* new_ret;
323
324 switch (resolved_hashtree_error_mode) {
325 case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
326 if (!cmdline_append_option(
327 slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
328 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
329 goto out;
330 }
331 verity_mode = "enforcing";
332 dm_verity_mode = "restart_on_corruption";
333 break;
334 case AVB_HASHTREE_ERROR_MODE_RESTART:
335 verity_mode = "enforcing";
336 dm_verity_mode = "restart_on_corruption";
337 break;
338 case AVB_HASHTREE_ERROR_MODE_EIO:
339 verity_mode = "eio";
340 /* For now there's no option to specify the EIO mode. So
341 * just use 'ignore_zero_blocks' since that's already set
342 * and dm-verity-target.c supports specifying this multiple
343 * times.
344 */
345 dm_verity_mode = "ignore_zero_blocks";
346 break;
347 case AVB_HASHTREE_ERROR_MODE_LOGGING:
348 verity_mode = "logging";
349 dm_verity_mode = "ignore_corruption";
350 break;
351 case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO:
352 // Should never get here because MANAGED_RESTART_AND_EIO is
353 // remapped by avb_manage_hashtree_error_mode().
354 avb_assert_not_reached();
355 break;
356 case AVB_HASHTREE_ERROR_MODE_PANIC:
357 verity_mode = "panicking";
358 dm_verity_mode = "panic_on_corruption";
359 break;
360 }
361 new_ret = avb_replace(
362 slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
363 avb_free(slot_data->cmdline);
364 slot_data->cmdline = new_ret;
365 if (slot_data->cmdline == NULL) {
366 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
367 goto out;
368 }
369 }
370 if (!cmdline_append_option(
371 slot_data, "androidboot.veritymode", verity_mode)) {
372 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
373 goto out;
374 }
375 if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
376 if (!cmdline_append_option(
377 slot_data, "androidboot.veritymode.managed", "yes")) {
378 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
379 goto out;
380 }
381 }
382
383 ret = AVB_SLOT_VERIFY_RESULT_OK;
384
385 out:
386
387 return ret;
388 }
389
avb_new_cmdline_subst_list()390 AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
391 return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
392 }
393
avb_free_cmdline_subst_list(AvbCmdlineSubstList * cmdline_subst)394 void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
395 size_t i;
396 for (i = 0; i < cmdline_subst->size; ++i) {
397 avb_free(cmdline_subst->tokens[i]);
398 avb_free(cmdline_subst->values[i]);
399 }
400 cmdline_subst->size = 0;
401 avb_free(cmdline_subst);
402 }
403
avb_add_root_digest_substitution(const char * part_name,const uint8_t * digest,size_t digest_size,AvbCmdlineSubstList * out_cmdline_subst)404 AvbSlotVerifyResult avb_add_root_digest_substitution(
405 const char* part_name,
406 const uint8_t* digest,
407 size_t digest_size,
408 AvbCmdlineSubstList* out_cmdline_subst) {
409 const char* kDigestSubPrefix = "$(AVB_";
410 const char* kDigestSubSuffix = "_ROOT_DIGEST)";
411 size_t part_name_len = avb_strlen(part_name);
412 size_t list_index = out_cmdline_subst->size;
413
414 avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE);
415 avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE);
416 if (part_name_len >= AVB_PART_NAME_MAX_SIZE ||
417 digest_size > AVB_SHA512_DIGEST_SIZE) {
418 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
419 }
420
421 if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) {
422 /* The list is full. Currently dynamic growth of this list is not supported.
423 */
424 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
425 }
426
427 /* Construct the token to replace in the command line based on the partition
428 * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'.
429 */
430 out_cmdline_subst->tokens[list_index] =
431 avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
432 if (out_cmdline_subst->tokens[list_index] == NULL) {
433 goto fail;
434 }
435 avb_uppercase(out_cmdline_subst->tokens[list_index]);
436
437 /* The digest value is hex encoded when inserted in the command line. */
438 out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size);
439 if (out_cmdline_subst->values[list_index] == NULL) {
440 goto fail;
441 }
442
443 out_cmdline_subst->size++;
444 return AVB_SLOT_VERIFY_RESULT_OK;
445
446 fail:
447 if (out_cmdline_subst->tokens[list_index]) {
448 avb_free(out_cmdline_subst->tokens[list_index]);
449 }
450 if (out_cmdline_subst->values[list_index]) {
451 avb_free(out_cmdline_subst->values[list_index]);
452 }
453 return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
454 }
455