1 /*
2  * Copyright (C) 2017 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 <stdio.h>
26 #include <string.h>
27 #include <sysexits.h>
28 
29 #include <android/hardware/boot/1.0/IBootControl.h>
30 
31 #include <libavb_user/libavb_user.h>
32 
33 using android::sp;
34 using android::hardware::hidl_string;
35 using android::hardware::Return;
36 using android::hardware::boot::V1_0::IBootControl;
37 using android::hardware::boot::V1_0::Slot;
38 
39 namespace {
40 
41 /* Prints program usage to |where|. */
usage(FILE * where,int,char * argv[])42 void usage(FILE* where, int /* argc */, char* argv[]) {
43   fprintf(where,
44           "%s - command-line tool for AVB.\n"
45           "\n"
46           "Usage:\n"
47           "  %s COMMAND\n"
48           "\n"
49           "Commands:\n"
50           "  %s disable-verity    - Disable verity in current slot.\n"
51           "  %s enable-verity     - Enable verity in current slot.\n",
52           argv[0],
53           argv[0],
54           argv[0],
55           argv[0]);
56 }
57 
58 /* Returns the A/B suffix the device booted from or the empty string
59  * if A/B is not in use.
60  */
get_ab_suffix(sp<IBootControl> module)61 std::string get_ab_suffix(sp<IBootControl> module) {
62   std::string suffix = "";
63 
64   if (module != nullptr) {
65     uint32_t num_slots = module->getNumberSlots();
66     if (num_slots > 1) {
67       Slot cur_slot = module->getCurrentSlot();
68       Return<void> ret =
69           module->getSuffix(cur_slot, [&suffix](const hidl_string& value) {
70             suffix = std::string(value.c_str());
71           });
72       if (!ret.isOk()) {
73         fprintf(stderr, "Error getting suffix for slot %d.\n", cur_slot);
74       }
75     }
76   }
77 
78   return suffix;
79 }
80 
81 /* Loads the toplevel AvbVBMetaImageHeader from the slot denoted by
82  * |ab_suffix| into |vbmeta_image|. No validation, verification, or
83  * byteswapping is performed.
84  *
85  * If successful, |true| is returned and the partition it was loaded
86  * from is returned in |out_partition_name| and the offset on said
87  * partition is returned in |out_vbmeta_offset|.
88  */
load_top_level_vbmeta_header(AvbOps * ops,const std::string & ab_suffix,uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE],std::string * out_partition_name,uint64_t * out_vbmeta_offset)89 bool load_top_level_vbmeta_header(
90     AvbOps* ops,
91     const std::string& ab_suffix,
92     uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE],
93     std::string* out_partition_name,
94     uint64_t* out_vbmeta_offset) {
95   std::string partition_name = std::string("vbmeta") + ab_suffix;
96   uint64_t vbmeta_offset = 0;
97 
98   // Only read the header.
99   size_t num_read;
100   AvbIOResult io_res = ops->read_from_partition(ops,
101                                                 partition_name.c_str(),
102                                                 vbmeta_offset,
103                                                 AVB_VBMETA_IMAGE_HEADER_SIZE,
104                                                 vbmeta_image,
105                                                 &num_read);
106   if (io_res == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
107     AvbFooter footer;
108     // Try looking for the vbmeta struct in 'boot' via the footer.
109     partition_name = std::string("boot") + ab_suffix;
110     io_res = ops->read_from_partition(ops,
111                                       partition_name.c_str(),
112                                       -AVB_FOOTER_SIZE,
113                                       AVB_FOOTER_SIZE,
114                                       &footer,
115                                       &num_read);
116     if (io_res != AVB_IO_RESULT_OK) {
117       fprintf(stderr,
118               "Error loading footer from partition '%s' (%d).\n",
119               partition_name.c_str(),
120               io_res);
121       return false;
122     }
123 
124     if (memcmp(footer.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) {
125       fprintf(stderr,
126               "Data from '%s' does not look like a vbmeta footer.\n",
127               partition_name.c_str());
128       return false;
129     }
130 
131     vbmeta_offset = avb_be64toh(footer.vbmeta_offset);
132     io_res = ops->read_from_partition(ops,
133                                       partition_name.c_str(),
134                                       vbmeta_offset,
135                                       AVB_VBMETA_IMAGE_HEADER_SIZE,
136                                       vbmeta_image,
137                                       &num_read);
138   }
139 
140   if (io_res != AVB_IO_RESULT_OK) {
141     fprintf(stderr,
142             "Error loading from offset %" PRIu64 " of partition '%s' (%d).\n",
143             vbmeta_offset,
144             partition_name.c_str(),
145             io_res);
146     return false;
147   }
148 
149   if (out_partition_name != nullptr) {
150     *out_partition_name = partition_name;
151   }
152   if (out_vbmeta_offset != nullptr) {
153     *out_vbmeta_offset = vbmeta_offset;
154   }
155   return true;
156 }
157 
158 /* Function to enable and disable dm-verity. The |ops| parameter
159  * should be an |AvbOps| from libavb_user and |module| can either be
160  * |nullptr| or a valid boot_control module.
161  */
do_set_verity(AvbOps * ops,sp<IBootControl> module,bool enable_verity)162 int do_set_verity(AvbOps* ops, sp<IBootControl> module, bool enable_verity) {
163   uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE];  // 256 bytes.
164   std::string ab_suffix;
165   std::string partition_name;
166   uint64_t vbmeta_offset;
167   AvbIOResult io_res;
168 
169   ab_suffix = get_ab_suffix(module);
170 
171   if (!load_top_level_vbmeta_header(
172           ops, ab_suffix, vbmeta_image, &partition_name, &vbmeta_offset)) {
173     return EX_SOFTWARE;
174   }
175 
176   if (memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
177     fprintf(stderr,
178             "Data from '%s' does not look like a vbmeta header.\n",
179             partition_name.c_str());
180     return EX_SOFTWARE;
181   }
182 
183   // Set/clear the HASHTREE_DISABLED bit, as requested.
184   AvbVBMetaImageHeader* header =
185       reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image);
186   uint32_t flags = avb_be32toh(header->flags);
187   flags &= ~AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED;
188   if (!enable_verity) {
189     flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED;
190   }
191   header->flags = avb_htobe32(flags);
192 
193   // Write the header.
194   io_res = ops->write_to_partition(ops,
195                                    partition_name.c_str(),
196                                    vbmeta_offset,
197                                    AVB_VBMETA_IMAGE_HEADER_SIZE,
198                                    vbmeta_image);
199   if (io_res != AVB_IO_RESULT_OK) {
200     fprintf(stderr,
201             "Error writing to offset %" PRIu64 " of partition '%s' (%d).\n",
202             vbmeta_offset,
203             partition_name.c_str(),
204             io_res);
205     return EX_SOFTWARE;
206   }
207 
208   fprintf(stdout,
209           "Successfully %s verity on %s.\n",
210           enable_verity ? "enabled" : "disabled",
211           partition_name.c_str());
212 
213   return EX_OK;
214 }
215 
216 }  // namespace
217 
main(int argc,char * argv[])218 int main(int argc, char* argv[]) {
219   int ret;
220   sp<IBootControl> module;
221   AvbOps* ops = nullptr;
222 
223   if (argc < 2) {
224     usage(stderr, argc, argv);
225     ret = EX_USAGE;
226     goto out;
227   }
228 
229   ops = avb_ops_user_new();
230   if (ops == nullptr) {
231     fprintf(stderr, "Error getting AVB ops.\n");
232     ret = EX_SOFTWARE;
233     goto out;
234   }
235 
236   // Failing to get the boot_control HAL is not a fatal error - it can
237   // happen if A/B is not in use, in which case |nullptr| is returned.
238   module = IBootControl::getService();
239 
240   if (strcmp(argv[1], "disable-verity") == 0) {
241     ret = do_set_verity(ops, module, false);
242   } else if (strcmp(argv[1], "enable-verity") == 0) {
243     ret = do_set_verity(ops, module, true);
244   } else {
245     usage(stderr, argc, argv);
246     ret = EX_USAGE;
247   }
248 
249   ret = EX_OK;
250 out:
251   if (ops != nullptr) {
252     avb_ops_user_free(ops);
253   }
254   return ret;
255 }
256