efi_loader: definition of GetNextVariableName()
[oweals/u-boot.git] / lib / libavb / avb_cmdline.c
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 <malloc.h>
11
12 #define NUM_GUIDS 3
13
14 /* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
15  * values. Returns NULL on OOM, otherwise the cmdline with values
16  * replaced.
17  */
18 char* avb_sub_cmdline(AvbOps* ops,
19                       const char* cmdline,
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)"};
27   char* ret = NULL;
28   AvbIOResult io_ret;
29   size_t n;
30
31   /* Special-case for when the top-level vbmeta struct is in the boot
32    * partition.
33    */
34   if (using_boot_for_vbmeta) {
35     part_name_str[2] = "boot";
36   }
37
38   /* Replace unique partition GUIDs */
39   for (n = 0; n < NUM_GUIDS; n++) {
40     char part_name[AVB_PART_NAME_MAX_SIZE];
41     char guid_buf[37];
42
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.
46      */
47     if (avb_strstr(cmdline, replace_str[n]) == NULL) {
48       continue;
49     }
50
51     if (!avb_str_concat(part_name,
52                         sizeof part_name,
53                         part_name_str[n],
54                         avb_strlen(part_name_str[n]),
55                         ab_suffix,
56                         avb_strlen(ab_suffix))) {
57       avb_error("Partition name and suffix does not fit.\n");
58       goto fail;
59     }
60
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) {
64       goto fail;
65     } else if (io_ret != AVB_IO_RESULT_OK) {
66       avb_error("Error getting unique GUID for partition.\n");
67       goto fail;
68     }
69
70     if (ret == NULL) {
71       ret = avb_replace(cmdline, replace_str[n], guid_buf);
72     } else {
73       char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
74       avb_free(ret);
75       ret = new_ret;
76     }
77     if (ret == NULL) {
78       goto fail;
79     }
80   }
81
82   /* It's possible there is no _PARTUUID for replacement above.
83    * Duplicate cmdline to ret for additional substitutions below.
84    */
85   if (ret == NULL) {
86     ret = avb_strdup(cmdline);
87     if (ret == NULL) {
88       goto fail;
89     }
90   }
91
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]);
98       avb_free(ret);
99       ret = new_ret;
100       if (ret == NULL) {
101         goto fail;
102       }
103     }
104   }
105
106   return ret;
107
108 fail:
109   if (ret != NULL) {
110     avb_free(ret);
111   }
112   return NULL;
113 }
114
115 static int cmdline_append_option(AvbSlotVerifyData* slot_data,
116                                  const char* key,
117                                  const char* value) {
118   size_t offset, key_len, value_len;
119   char* new_cmdline;
120
121   key_len = avb_strlen(key);
122   value_len = avb_strlen(value);
123
124   offset = 0;
125   if (slot_data->cmdline != NULL) {
126     offset = avb_strlen(slot_data->cmdline);
127     if (offset > 0) {
128       offset += 1;
129     }
130   }
131
132   new_cmdline = avb_calloc(offset + key_len + value_len + 2);
133   if (new_cmdline == NULL) {
134     return 0;
135   }
136   if (offset > 0) {
137     avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
138     new_cmdline[offset - 1] = ' ';
139   }
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);
145   }
146   slot_data->cmdline = new_cmdline;
147
148   return 1;
149 }
150
151 #define AVB_MAX_DIGITS_UINT64 32
152
153 /* Writes |value| to |digits| in base 10 followed by a NUL byte.
154  * Returns number of characters written excluding the NUL byte.
155  */
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;
160
161   for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
162     rev_digits[num_digits++] = avb_div_by_10(&value) + '0';
163     if (value == 0) {
164       break;
165     }
166   }
167
168   for (n = 0; n < num_digits; n++) {
169     digits[n] = rev_digits[num_digits - 1 - n];
170   }
171   digits[n] = '\0';
172   return n;
173 }
174
175 static int cmdline_append_version(AvbSlotVerifyData* slot_data,
176                                   const char* key,
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;
183
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';
190
191   return cmdline_append_option(slot_data, key, combined);
192 }
193
194 static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
195                                         const char* key,
196                                         uint64_t value) {
197   char digits[AVB_MAX_DIGITS_UINT64];
198   uint64_to_base10(value, digits);
199   return cmdline_append_option(slot_data, key, digits);
200 }
201
202 static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
203                               const char* key,
204                               const uint8_t* data,
205                               size_t data_len) {
206   int ret;
207   char* hex_data = avb_bin2hex(data, data_len);
208   if (hex_data == NULL) {
209     return 0;
210   }
211   ret = cmdline_append_option(slot_data, key, hex_data);
212   avb_free(hex_data);
213   return ret;
214 }
215
216 AvbSlotVerifyResult avb_append_options(
217     AvbOps* ops,
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;
227   AvbIOResult io_ret;
228
229   /* Add androidboot.vbmeta.device option... except if not using a vbmeta
230    * partition since it doesn't make sense in that case.
231    */
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;
237       goto out;
238     }
239   }
240
241   /* Add androidboot.vbmeta.avb_version option. */
242   if (!cmdline_append_version(slot_data,
243                               "androidboot.vbmeta.avb_version",
244                               AVB_VERSION_MAJOR,
245                               AVB_VERSION_MINOR)) {
246     ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
247     goto out;
248   }
249
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;
254     goto out;
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;
258     goto out;
259   }
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;
264     goto out;
265   }
266
267   /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
268    * function as is used to sign vbmeta.
269    */
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;
282       }
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",
289                               vbmeta_digest,
290                               AVB_SHA256_DIGEST_SIZE)) {
291         ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
292         goto out;
293       }
294     } break;
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;
305       }
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",
312                               vbmeta_digest,
313                               AVB_SHA512_DIGEST_SIZE)) {
314         ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
315         goto out;
316       }
317     } break;
318     case _AVB_ALGORITHM_NUM_TYPES:
319       avb_assert_not_reached();
320       break;
321   }
322
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";
326   } else {
327     const char* dm_verity_mode;
328     char* new_ret;
329
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;
335           goto out;
336         }
337         verity_mode = "enforcing";
338         dm_verity_mode = "restart_on_corruption";
339         break;
340       case AVB_HASHTREE_ERROR_MODE_RESTART:
341         verity_mode = "enforcing";
342         dm_verity_mode = "restart_on_corruption";
343         break;
344       case AVB_HASHTREE_ERROR_MODE_EIO:
345         verity_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
349          * times.
350          */
351         dm_verity_mode = "ignore_zero_blocks";
352         break;
353       case AVB_HASHTREE_ERROR_MODE_LOGGING:
354         verity_mode = "logging";
355         dm_verity_mode = "ignore_corruption";
356         break;
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;
362         goto out;
363       default:
364         ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
365         goto out;
366     }
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;
373       goto out;
374     }
375   }
376   if (!cmdline_append_option(
377           slot_data, "androidboot.veritymode", verity_mode)) {
378     ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
379     goto out;
380   }
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;
385       goto out;
386     }
387   }
388
389   ret = AVB_SLOT_VERIFY_RESULT_OK;
390
391 out:
392
393   return ret;
394 }
395
396 AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
397   return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
398 }
399
400 void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
401   size_t i;
402   for (i = 0; i < cmdline_subst->size; ++i) {
403     avb_free(cmdline_subst->tokens[i]);
404     avb_free(cmdline_subst->values[i]);
405   }
406   cmdline_subst->size = 0;
407   avb_free(cmdline_subst);
408 }
409
410 AvbSlotVerifyResult avb_add_root_digest_substitution(
411     const char* part_name,
412     const uint8_t* digest,
413     size_t digest_size,
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;
419
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;
425   }
426
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.
429      */
430     return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
431   }
432
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)'.
435    */
436   out_cmdline_subst->tokens[list_index] =
437       avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
438   if (out_cmdline_subst->tokens[list_index] == NULL) {
439     goto fail;
440   }
441   avb_uppercase(out_cmdline_subst->tokens[list_index]);
442
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) {
446     goto fail;
447   }
448
449   out_cmdline_subst->size++;
450   return AVB_SLOT_VERIFY_RESULT_OK;
451
452 fail:
453   if (out_cmdline_subst->tokens[list_index]) {
454     avb_free(out_cmdline_subst->tokens[list_index]);
455   }
456   if (out_cmdline_subst->values[list_index]) {
457     avb_free(out_cmdline_subst->values[list_index]);
458   }
459   return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
460 }