1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "apex_file.h"
18
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <filesystem>
25 #include <fstream>
26
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/scopeguard.h>
30 #include <android-base/strings.h>
31 #include <android-base/unique_fd.h>
32 #include <google/protobuf/util/message_differencer.h>
33 #include <libavb/libavb.h>
34
35 #include "apex_constants.h"
36 #include "apex_preinstalled_data.h"
37 #include "apexd_utils.h"
38 #include "string_log.h"
39
40 using android::base::EndsWith;
41 using android::base::Error;
42 using android::base::ReadFullyAtOffset;
43 using android::base::Result;
44 using android::base::StartsWith;
45 using android::base::unique_fd;
46 using google::protobuf::util::MessageDifferencer;
47
48 namespace android {
49 namespace apex {
50 namespace {
51
52 constexpr const char* kImageFilename = "apex_payload.img";
53 constexpr const char* kBundledPublicKeyFilename = "apex_pubkey";
54
55 } // namespace
56
Open(const std::string & path)57 Result<ApexFile> ApexFile::Open(const std::string& path) {
58 int32_t image_offset;
59 size_t image_size;
60 std::string manifest_content;
61 std::string pubkey;
62
63 ZipArchiveHandle handle;
64 auto handle_guard =
65 android::base::make_scope_guard([&handle] { CloseArchive(handle); });
66 int ret = OpenArchive(path.c_str(), &handle);
67 if (ret < 0) {
68 return Error() << "Failed to open package " << path << ": "
69 << ErrorCodeString(ret);
70 }
71
72 // Locate the mountable image within the zipfile and store offset and size.
73 ZipEntry entry;
74 ret = FindEntry(handle, kImageFilename, &entry);
75 if (ret < 0) {
76 return Error() << "Could not find entry \"" << kImageFilename
77 << "\" in package " << path << ": " << ErrorCodeString(ret);
78 }
79 image_offset = entry.offset;
80 image_size = entry.uncompressed_length;
81
82 ret = FindEntry(handle, kManifestFilenamePb, &entry);
83 if (ret < 0) {
84 return Error() << "Could not find entry \"" << kManifestFilenamePb
85 << "\" in package " << path << ": " << ErrorCodeString(ret);
86 }
87
88 uint32_t length = entry.uncompressed_length;
89 manifest_content.resize(length, '\0');
90 ret = ExtractToMemory(handle, &entry,
91 reinterpret_cast<uint8_t*>(&(manifest_content)[0]),
92 length);
93 if (ret != 0) {
94 return Error() << "Failed to extract manifest from package " << path << ": "
95 << ErrorCodeString(ret);
96 }
97
98 ret = FindEntry(handle, kBundledPublicKeyFilename, &entry);
99 if (ret >= 0) {
100 length = entry.uncompressed_length;
101 pubkey.resize(length, '\0');
102 ret = ExtractToMemory(handle, &entry,
103 reinterpret_cast<uint8_t*>(&(pubkey)[0]), length);
104 if (ret != 0) {
105 return Error() << "Failed to extract public key from package " << path
106 << ": " << ErrorCodeString(ret);
107 }
108 }
109
110 Result<ApexManifest> manifest = ParseManifest(manifest_content);
111 if (!manifest.ok()) {
112 return manifest.error();
113 }
114
115 return ApexFile(path, image_offset, image_size, std::move(*manifest), pubkey,
116 isPathForBuiltinApexes(path));
117 }
118
119 // AVB-related code.
120
121 namespace {
122
123 static constexpr int kVbMetaMaxSize = 64 * 1024;
124
bytes_to_hex(const uint8_t * bytes,size_t bytes_len)125 std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
126 std::ostringstream s;
127
128 s << std::hex << std::setfill('0');
129 for (size_t i = 0; i < bytes_len; i++) {
130 s << std::setw(2) << static_cast<int>(bytes[i]);
131 }
132 return s.str();
133 }
134
getSalt(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)135 std::string getSalt(const AvbHashtreeDescriptor& desc,
136 const uint8_t* trailingData) {
137 const uint8_t* desc_salt = trailingData + desc.partition_name_len;
138
139 return bytes_to_hex(desc_salt, desc.salt_len);
140 }
141
getDigest(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)142 std::string getDigest(const AvbHashtreeDescriptor& desc,
143 const uint8_t* trailingData) {
144 const uint8_t* desc_digest =
145 trailingData + desc.partition_name_len + desc.salt_len;
146
147 return bytes_to_hex(desc_digest, desc.root_digest_len);
148 }
149
getAvbFooter(const ApexFile & apex,const unique_fd & fd)150 Result<std::unique_ptr<AvbFooter>> getAvbFooter(const ApexFile& apex,
151 const unique_fd& fd) {
152 std::array<uint8_t, AVB_FOOTER_SIZE> footer_data;
153 auto footer = std::make_unique<AvbFooter>();
154
155 // The AVB footer is located in the last part of the image
156 off_t offset = apex.GetImageSize() + apex.GetImageOffset() - AVB_FOOTER_SIZE;
157 int ret = lseek(fd, offset, SEEK_SET);
158 if (ret == -1) {
159 return ErrnoError() << "Couldn't seek to AVB footer";
160 }
161
162 ret = read(fd, footer_data.data(), AVB_FOOTER_SIZE);
163 if (ret != AVB_FOOTER_SIZE) {
164 return ErrnoError() << "Couldn't read AVB footer";
165 }
166
167 if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_data.data(),
168 footer.get())) {
169 return Error() << "AVB footer verification failed.";
170 }
171
172 LOG(VERBOSE) << "AVB footer verification successful.";
173 return footer;
174 }
175
CompareKeys(const uint8_t * key,size_t length,const std::string & public_key_content)176 bool CompareKeys(const uint8_t* key, size_t length,
177 const std::string& public_key_content) {
178 return public_key_content.length() == length &&
179 memcmp(&public_key_content[0], key, length) == 0;
180 }
181
verifyVbMetaSignature(const ApexFile & apex,const uint8_t * data,size_t length)182 Result<void> verifyVbMetaSignature(const ApexFile& apex, const uint8_t* data,
183 size_t length) {
184 const uint8_t* pk;
185 size_t pk_len;
186 AvbVBMetaVerifyResult res;
187
188 res = avb_vbmeta_image_verify(data, length, &pk, &pk_len);
189 switch (res) {
190 case AVB_VBMETA_VERIFY_RESULT_OK:
191 break;
192 case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
193 case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
194 case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
195 return Error() << "Error verifying " << apex.GetPath() << ": "
196 << avb_vbmeta_verify_result_to_string(res);
197 case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
198 return Error() << "Error verifying " << apex.GetPath() << ": "
199 << "invalid vbmeta header";
200 case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
201 return Error() << "Error verifying " << apex.GetPath() << ": "
202 << "unsupported version";
203 default:
204 return Errorf("Unknown vmbeta_image_verify return value");
205 }
206
207 Result<const std::string> public_key = getApexKey(apex.GetManifest().name());
208 if (public_key.ok()) {
209 // TODO(b/115718846)
210 // We need to decide whether we need rollback protection, and whether
211 // we can use the rollback protection provided by libavb.
212 if (!CompareKeys(pk, pk_len, *public_key)) {
213 return Error() << "Error verifying " << apex.GetPath() << ": "
214 << "public key doesn't match the pre-installed one";
215 }
216 } else {
217 return public_key.error();
218 }
219 LOG(VERBOSE) << apex.GetPath() << ": public key matches.";
220 return {};
221 }
222
verifyVbMeta(const ApexFile & apex,const unique_fd & fd,const AvbFooter & footer)223 Result<std::unique_ptr<uint8_t[]>> verifyVbMeta(const ApexFile& apex,
224 const unique_fd& fd,
225 const AvbFooter& footer) {
226 if (footer.vbmeta_size > kVbMetaMaxSize) {
227 return Errorf("VbMeta size in footer exceeds kVbMetaMaxSize.");
228 }
229
230 off_t offset = apex.GetImageOffset() + footer.vbmeta_offset;
231 std::unique_ptr<uint8_t[]> vbmeta_buf(new uint8_t[footer.vbmeta_size]);
232
233 if (!ReadFullyAtOffset(fd, vbmeta_buf.get(), footer.vbmeta_size, offset)) {
234 return ErrnoError() << "Couldn't read AVB meta-data";
235 }
236
237 Result<void> st =
238 verifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size);
239 if (!st.ok()) {
240 return st.error();
241 }
242
243 return vbmeta_buf;
244 }
245
findDescriptor(uint8_t * vbmeta_data,size_t vbmeta_size)246 Result<const AvbHashtreeDescriptor*> findDescriptor(uint8_t* vbmeta_data,
247 size_t vbmeta_size) {
248 const AvbDescriptor** descriptors;
249 size_t num_descriptors;
250
251 descriptors =
252 avb_descriptor_get_all(vbmeta_data, vbmeta_size, &num_descriptors);
253
254 // avb_descriptor_get_all() returns an internally allocated array
255 // of pointers and it needs to be avb_free()ed after using it.
256 auto guard = android::base::ScopeGuard(std::bind(avb_free, descriptors));
257
258 for (size_t i = 0; i < num_descriptors; i++) {
259 AvbDescriptor desc;
260 if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
261 return Errorf("Couldn't validate AvbDescriptor.");
262 }
263
264 if (desc.tag != AVB_DESCRIPTOR_TAG_HASHTREE) {
265 // Ignore other descriptors
266 continue;
267 }
268
269 // Check that hashtree descriptor actually fits into memory.
270 const uint8_t* vbmeta_end = vbmeta_data + vbmeta_size;
271 if ((uint8_t*)descriptors[i] + sizeof(AvbHashtreeDescriptor) > vbmeta_end) {
272 return Errorf("Invalid length for AvbHashtreeDescriptor");
273 }
274 return (const AvbHashtreeDescriptor*)descriptors[i];
275 }
276
277 return Errorf("Couldn't find any AVB hashtree descriptors.");
278 }
279
verifyDescriptor(const AvbHashtreeDescriptor * desc)280 Result<std::unique_ptr<AvbHashtreeDescriptor>> verifyDescriptor(
281 const AvbHashtreeDescriptor* desc) {
282 auto verifiedDesc = std::make_unique<AvbHashtreeDescriptor>();
283
284 if (!avb_hashtree_descriptor_validate_and_byteswap(desc,
285 verifiedDesc.get())) {
286 return Errorf("Couldn't validate AvbDescriptor.");
287 }
288
289 return verifiedDesc;
290 }
291
292 } // namespace
293
VerifyApexVerity() const294 Result<ApexVerityData> ApexFile::VerifyApexVerity() const {
295 ApexVerityData verityData;
296
297 unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
298 if (fd.get() == -1) {
299 return ErrnoError() << "Failed to open " << GetPath();
300 }
301
302 Result<std::unique_ptr<AvbFooter>> footer = getAvbFooter(*this, fd);
303 if (!footer.ok()) {
304 return footer.error();
305 }
306
307 Result<std::unique_ptr<uint8_t[]>> vbmeta_data =
308 verifyVbMeta(*this, fd, **footer);
309 if (!vbmeta_data.ok()) {
310 return vbmeta_data.error();
311 }
312
313 Result<const AvbHashtreeDescriptor*> descriptor =
314 findDescriptor(vbmeta_data->get(), (*footer)->vbmeta_size);
315 if (!descriptor.ok()) {
316 return descriptor.error();
317 }
318
319 Result<std::unique_ptr<AvbHashtreeDescriptor>> verifiedDescriptor =
320 verifyDescriptor(*descriptor);
321 if (!verifiedDescriptor.ok()) {
322 return verifiedDescriptor.error();
323 }
324 verityData.desc = std::move(*verifiedDescriptor);
325
326 // This area is now safe to access, because we just verified it
327 const uint8_t* trailingData =
328 (const uint8_t*)*descriptor + sizeof(AvbHashtreeDescriptor);
329 verityData.hash_algorithm =
330 reinterpret_cast<const char*>((*descriptor)->hash_algorithm);
331 verityData.salt = getSalt(*verityData.desc, trailingData);
332 verityData.root_digest = getDigest(*verityData.desc, trailingData);
333
334 return verityData;
335 }
336
VerifyManifestMatches(const std::string & mount_path) const337 Result<void> ApexFile::VerifyManifestMatches(
338 const std::string& mount_path) const {
339 Result<ApexManifest> verifiedManifest =
340 ReadManifest(mount_path + "/" + kManifestFilenamePb);
341 if (!verifiedManifest.ok()) {
342 return verifiedManifest.error();
343 }
344
345 if (!MessageDifferencer::Equals(manifest_, *verifiedManifest)) {
346 return Errorf(
347 "Manifest inside filesystem does not match manifest outside it");
348 }
349
350 return {};
351 }
352
FindApexes(const std::vector<std::string> & paths)353 Result<std::vector<std::string>> FindApexes(
354 const std::vector<std::string>& paths) {
355 std::vector<std::string> result;
356 for (const auto& path : paths) {
357 auto exist = PathExists(path);
358 if (!exist.ok()) {
359 return exist.error();
360 }
361 if (!*exist) continue;
362
363 const auto& apexes = FindApexFilesByName(path);
364 if (!apexes.ok()) {
365 return apexes;
366 }
367
368 result.insert(result.end(), apexes->begin(), apexes->end());
369 }
370 return result;
371 }
372
FindApexFilesByName(const std::string & path)373 Result<std::vector<std::string>> FindApexFilesByName(const std::string& path) {
374 auto filter_fn = [](const std::filesystem::directory_entry& entry) {
375 std::error_code ec;
376 if (entry.is_regular_file(ec) &&
377 EndsWith(entry.path().filename().string(), kApexPackageSuffix)) {
378 return true; // APEX file, take.
379 }
380 return false;
381 };
382 return ReadDir(path, filter_fn);
383 }
384
isPathForBuiltinApexes(const std::string & path)385 bool isPathForBuiltinApexes(const std::string& path) {
386 for (const auto& dir : kApexPackageBuiltinDirs) {
387 if (StartsWith(path, dir)) {
388 return true;
389 }
390 }
391 return false;
392 }
393
394 } // namespace apex
395 } // namespace android
396