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_key.h"
36 #include "apexd_utils.h"
37 #include "string_log.h"
38
39 using android::base::EndsWith;
40 using android::base::ReadFullyAtOffset;
41 using android::base::StartsWith;
42 using android::base::unique_fd;
43 using google::protobuf::util::MessageDifferencer;
44
45 namespace android {
46 namespace apex {
47 namespace {
48
49 constexpr const char* kImageFilename = "apex_payload.img";
50 constexpr const char* kManifestFilename = "apex_manifest.json";
51 constexpr const char* kBundledPublicKeyFilename = "apex_pubkey";
52 #ifdef DEBUG_ALLOW_BUNDLED_KEY
53 constexpr const bool kDebugAllowBundledKey = true;
54 #else
55 constexpr const bool kDebugAllowBundledKey = false;
56 #endif
57
58 } // namespace
59
60 // Tests if <path>/manifest.json file exists.
isFlattenedApex(const std::string & path)61 bool isFlattenedApex(const std::string& path) {
62 struct stat buf;
63 const std::string manifest = path + "/" + kManifestFilename;
64 if (stat(manifest.c_str(), &buf) != 0) {
65 if (errno == ENOENT) {
66 return false;
67 }
68 // If the APEX is there but not a flatttened apex, the final component
69 // of path will be a file, and stat will complain that it's not a directory.
70 // We are OK with that to avoid two stat calls.
71 if (errno != ENOTDIR) {
72 PLOG(ERROR) << "Failed to stat " << path;
73 }
74 return false;
75 }
76
77 if (!S_ISREG(buf.st_mode)) {
78 return false;
79 }
80 return true;
81 }
82
Open(const std::string & path)83 StatusOr<ApexFile> ApexFile::Open(const std::string& path) {
84 bool flattened;
85 int32_t image_offset;
86 size_t image_size;
87 std::string manifest_content;
88 std::string pubkey;
89
90 if (isFlattenedApex(path)) {
91 flattened = true;
92 image_offset = 0;
93 image_size = 0;
94 const std::string manifest_path = path + "/" + kManifestFilename;
95 if (!android::base::ReadFileToString(manifest_path, &manifest_content)) {
96 std::string err = StringLog()
97 << "Failed to read manifest file: " << manifest_path;
98 return StatusOr<ApexFile>::MakeError(err);
99 }
100 // TODO(b/124115379) don't read public key from flattened APEX when
101 // we no longer have APEX tests on devices with flattened APEXes.
102 const std::string pubkey_path = path + "/" + kBundledPublicKeyFilename;
103 if (access(pubkey_path.c_str(), F_OK) == 0) {
104 if (!android::base::ReadFileToString(pubkey_path, &pubkey)) {
105 std::string err = StringLog()
106 << "Failed to read pubkey file: " << pubkey_path;
107 return StatusOr<ApexFile>::MakeError(err);
108 }
109 }
110 } else {
111 flattened = false;
112
113 ZipArchiveHandle handle;
114 auto handle_guard =
115 android::base::make_scope_guard([&handle] { CloseArchive(handle); });
116 int ret = OpenArchive(path.c_str(), &handle);
117 if (ret < 0) {
118 std::string err = StringLog() << "Failed to open package " << path << ": "
119 << ErrorCodeString(ret);
120 return StatusOr<ApexFile>::MakeError(err);
121 }
122
123 // Locate the mountable image within the zipfile and store offset and size.
124 ZipEntry entry;
125 ret = FindEntry(handle, ZipString(kImageFilename), &entry);
126 if (ret < 0) {
127 std::string err = StringLog() << "Could not find entry \""
128 << kImageFilename << "\" in package "
129 << path << ": " << ErrorCodeString(ret);
130 return StatusOr<ApexFile>::MakeError(err);
131 }
132 image_offset = entry.offset;
133 image_size = entry.uncompressed_length;
134
135 ret = FindEntry(handle, ZipString(kManifestFilename), &entry);
136 if (ret < 0) {
137 std::string err = StringLog() << "Could not find entry \""
138 << kManifestFilename << "\" in package "
139 << path << ": " << ErrorCodeString(ret);
140 return StatusOr<ApexFile>::MakeError(err);
141 }
142
143 uint32_t length = entry.uncompressed_length;
144 manifest_content.resize(length, '\0');
145 ret = ExtractToMemory(handle, &entry,
146 reinterpret_cast<uint8_t*>(&(manifest_content)[0]),
147 length);
148 if (ret != 0) {
149 std::string err = StringLog()
150 << "Failed to extract manifest from package " << path
151 << ": " << ErrorCodeString(ret);
152 return StatusOr<ApexFile>::MakeError(err);
153 }
154
155 ret = FindEntry(handle, ZipString(kBundledPublicKeyFilename), &entry);
156 if (ret >= 0) {
157 LOG(VERBOSE) << "Found bundled key in package " << path;
158 length = entry.uncompressed_length;
159 pubkey.resize(length, '\0');
160 ret = ExtractToMemory(handle, &entry,
161 reinterpret_cast<uint8_t*>(&(pubkey)[0]), length);
162 if (ret != 0) {
163 std::string err = StringLog()
164 << "Failed to extract public key from package "
165 << path << ": " << ErrorCodeString(ret);
166 return StatusOr<ApexFile>::MakeError(err);
167 }
168 }
169 }
170
171 StatusOr<ApexManifest> manifest = ParseManifest(manifest_content);
172 if (!manifest.Ok()) {
173 return StatusOr<ApexFile>::MakeError(manifest.ErrorMessage());
174 }
175
176 ApexFile apexFile(path, flattened, image_offset, image_size, *manifest,
177 pubkey);
178 return StatusOr<ApexFile>(std::move(apexFile));
179 }
180
181 // AVB-related code.
182
183 namespace {
184
185 static constexpr const char* kApexKeyProp = "apex.key";
186
187 static constexpr int kVbMetaMaxSize = 64 * 1024;
188
bytes_to_hex(const uint8_t * bytes,size_t bytes_len)189 std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
190 std::ostringstream s;
191
192 s << std::hex << std::setfill('0');
193 for (size_t i = 0; i < bytes_len; i++) {
194 s << std::setw(2) << static_cast<int>(bytes[i]);
195 }
196 return s.str();
197 }
198
getSalt(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)199 std::string getSalt(const AvbHashtreeDescriptor& desc,
200 const uint8_t* trailingData) {
201 const uint8_t* desc_salt = trailingData + desc.partition_name_len;
202
203 return bytes_to_hex(desc_salt, desc.salt_len);
204 }
205
getDigest(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)206 std::string getDigest(const AvbHashtreeDescriptor& desc,
207 const uint8_t* trailingData) {
208 const uint8_t* desc_digest =
209 trailingData + desc.partition_name_len + desc.salt_len;
210
211 return bytes_to_hex(desc_digest, desc.root_digest_len);
212 }
213
getAvbFooter(const ApexFile & apex,const unique_fd & fd)214 StatusOr<std::unique_ptr<AvbFooter>> getAvbFooter(const ApexFile& apex,
215 const unique_fd& fd) {
216 std::array<uint8_t, AVB_FOOTER_SIZE> footer_data;
217 auto footer = std::make_unique<AvbFooter>();
218
219 // The AVB footer is located in the last part of the image
220 off_t offset = apex.GetImageSize() + apex.GetImageOffset() - AVB_FOOTER_SIZE;
221 int ret = lseek(fd, offset, SEEK_SET);
222 if (ret == -1) {
223 return StatusOr<std::unique_ptr<AvbFooter>>::MakeError(
224 PStringLog() << "Couldn't seek to AVB footer");
225 }
226
227 ret = read(fd, footer_data.data(), AVB_FOOTER_SIZE);
228 if (ret != AVB_FOOTER_SIZE) {
229 return StatusOr<std::unique_ptr<AvbFooter>>::MakeError(
230 PStringLog() << "Couldn't read AVB footer");
231 }
232
233 if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_data.data(),
234 footer.get())) {
235 return StatusOr<std::unique_ptr<AvbFooter>>::MakeError(
236 StringLog() << "AVB footer verification failed.");
237 }
238
239 LOG(VERBOSE) << "AVB footer verification successful.";
240 return StatusOr<std::unique_ptr<AvbFooter>>(std::move(footer));
241 }
242
verifyPublicKey(const uint8_t * key,size_t length,std::string public_key_content)243 Status verifyPublicKey(const uint8_t* key, size_t length,
244 std::string public_key_content) {
245 if (public_key_content.length() != length ||
246 memcmp(&public_key_content[0], key, length) != 0) {
247 return Status::Fail("Failed to compare the bundled public key with key");
248 }
249 return Status::Success();
250 }
251
getPublicKeyName(const ApexFile & apex,const uint8_t * data,size_t length)252 StatusOr<std::string> getPublicKeyName(const ApexFile& apex,
253 const uint8_t* data, size_t length) {
254 size_t keyNameLen;
255 const char* keyName = avb_property_lookup(data, length, kApexKeyProp,
256 strlen(kApexKeyProp), &keyNameLen);
257 if (keyName == nullptr || keyNameLen == 0) {
258 return StatusOr<std::string>::MakeError(
259 StringLog() << "Cannot find prop '" << kApexKeyProp << "' from "
260 << apex.GetPath());
261 }
262
263 if (keyName != apex.GetManifest().name()) {
264 return StatusOr<std::string>::MakeError(
265 StringLog() << "Key mismatch: apex name is '"
266 << apex.GetManifest().name() << "'"
267 << " but key name is '" << keyName << "'");
268 }
269 return StatusOr<std::string>(keyName);
270 }
271
verifyVbMetaSignature(const ApexFile & apex,const uint8_t * data,size_t length)272 Status verifyVbMetaSignature(const ApexFile& apex, const uint8_t* data,
273 size_t length) {
274 const uint8_t* pk;
275 size_t pk_len;
276 AvbVBMetaVerifyResult res;
277
278 res = avb_vbmeta_image_verify(data, length, &pk, &pk_len);
279 switch (res) {
280 case AVB_VBMETA_VERIFY_RESULT_OK:
281 break;
282 case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
283 case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
284 case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
285 return Status::Fail(StringLog()
286 << "Error verifying " << apex.GetPath() << ": "
287 << avb_vbmeta_verify_result_to_string(res));
288 case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
289 return Status::Fail(StringLog()
290 << "Error verifying " << apex.GetPath() << ": "
291 << "invalid vbmeta header");
292 case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
293 return Status::Fail(StringLog()
294 << "Error verifying " << apex.GetPath() << ": "
295 << "unsupported version");
296 default:
297 return Status::Fail("Unknown vmbeta_image_verify return value");
298 }
299
300 StatusOr<std::string> key_name = getPublicKeyName(apex, data, length);
301 if (!key_name.Ok()) {
302 return key_name.ErrorStatus();
303 }
304
305 StatusOr<const std::string> public_key = getApexKey(*key_name);
306 Status st;
307 if (public_key.Ok()) {
308 // TODO(b/115718846)
309 // We need to decide whether we need rollback protection, and whether
310 // we can use the rollback protection provided by libavb.
311 st = verifyPublicKey(pk, pk_len, *public_key);
312 } else if (kDebugAllowBundledKey) {
313 // Failing to find the matching public key in the built-in partitions
314 // is a hard error for non-debuggable build. For debuggable builds,
315 // the public key bundled in the APEX itself is used as a fallback.
316 LOG(WARNING) << "Verifying " << apex.GetPath() << " with the bundled key";
317 st = verifyPublicKey(pk, pk_len, apex.GetBundledPublicKey());
318 } else {
319 return public_key.ErrorStatus();
320 }
321
322 if (st.Ok()) {
323 LOG(VERBOSE) << apex.GetPath() << ": public key matches.";
324 return st;
325 }
326
327 return Status::Fail(StringLog()
328 << "Error verifying " << apex.GetPath() << ": "
329 << "couldn't verify public key: " << st.ErrorMessage());
330 }
331
verifyVbMeta(const ApexFile & apex,const unique_fd & fd,const AvbFooter & footer)332 StatusOr<std::unique_ptr<uint8_t[]>> verifyVbMeta(const ApexFile& apex,
333 const unique_fd& fd,
334 const AvbFooter& footer) {
335 if (footer.vbmeta_size > kVbMetaMaxSize) {
336 return StatusOr<std::unique_ptr<uint8_t[]>>::MakeError(
337 "VbMeta size in footer exceeds kVbMetaMaxSize.");
338 }
339
340 off_t offset = apex.GetImageOffset() + footer.vbmeta_offset;
341 std::unique_ptr<uint8_t[]> vbmeta_buf(new uint8_t[footer.vbmeta_size]);
342
343 if (!ReadFullyAtOffset(fd, vbmeta_buf.get(), footer.vbmeta_size, offset)) {
344 return StatusOr<std::unique_ptr<uint8_t[]>>::MakeError(
345 PStringLog() << "Couldn't read AVB meta-data");
346 }
347
348 Status st = verifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size);
349 if (!st.Ok()) {
350 return StatusOr<std::unique_ptr<uint8_t[]>>::MakeError(st.ErrorMessage());
351 }
352
353 return StatusOr<std::unique_ptr<uint8_t[]>>(std::move(vbmeta_buf));
354 }
355
findDescriptor(uint8_t * vbmeta_data,size_t vbmeta_size)356 StatusOr<const AvbHashtreeDescriptor*> findDescriptor(uint8_t* vbmeta_data,
357 size_t vbmeta_size) {
358 const AvbDescriptor** descriptors;
359 size_t num_descriptors;
360
361 descriptors =
362 avb_descriptor_get_all(vbmeta_data, vbmeta_size, &num_descriptors);
363
364 // avb_descriptor_get_all() returns an internally allocated array
365 // of pointers and it needs to be avb_free()ed after using it.
366 auto guard = android::base::ScopeGuard(std::bind(avb_free, descriptors));
367
368 for (size_t i = 0; i < num_descriptors; i++) {
369 AvbDescriptor desc;
370 if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
371 return StatusOr<const AvbHashtreeDescriptor*>::MakeError(
372 "Couldn't validate AvbDescriptor.");
373 }
374
375 if (desc.tag != AVB_DESCRIPTOR_TAG_HASHTREE) {
376 // Ignore other descriptors
377 continue;
378 }
379
380 return StatusOr<const AvbHashtreeDescriptor*>(
381 (const AvbHashtreeDescriptor*)descriptors[i]);
382 }
383
384 return StatusOr<const AvbHashtreeDescriptor*>::MakeError(
385 "Couldn't find any AVB hashtree descriptors.");
386 }
387
verifyDescriptor(const AvbHashtreeDescriptor * desc)388 StatusOr<std::unique_ptr<AvbHashtreeDescriptor>> verifyDescriptor(
389 const AvbHashtreeDescriptor* desc) {
390 auto verifiedDesc = std::make_unique<AvbHashtreeDescriptor>();
391
392 if (!avb_hashtree_descriptor_validate_and_byteswap(desc,
393 verifiedDesc.get())) {
394 return StatusOr<std::unique_ptr<AvbHashtreeDescriptor>>::MakeError(
395 "Couldn't validate AvbDescriptor.");
396 }
397
398 return StatusOr<std::unique_ptr<AvbHashtreeDescriptor>>(
399 std::move(verifiedDesc));
400 }
401
402 } // namespace
403
VerifyApexVerity() const404 StatusOr<ApexVerityData> ApexFile::VerifyApexVerity() const {
405 ApexVerityData verityData;
406
407 unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
408 if (fd.get() == -1) {
409 return StatusOr<ApexVerityData>::MakeError(PStringLog() << "Failed to open "
410 << GetPath());
411 }
412
413 StatusOr<std::unique_ptr<AvbFooter>> footer = getAvbFooter(*this, fd);
414 if (!footer.Ok()) {
415 return StatusOr<ApexVerityData>::MakeError(footer.ErrorMessage());
416 }
417
418 StatusOr<std::unique_ptr<uint8_t[]>> vbmeta_data =
419 verifyVbMeta(*this, fd, **footer);
420 if (!vbmeta_data.Ok()) {
421 return StatusOr<ApexVerityData>::MakeError(vbmeta_data.ErrorMessage());
422 }
423
424 StatusOr<const AvbHashtreeDescriptor*> descriptor =
425 findDescriptor(vbmeta_data->get(), (*footer)->vbmeta_size);
426 if (!descriptor.Ok()) {
427 return StatusOr<ApexVerityData>::MakeError(descriptor.ErrorMessage());
428 }
429
430 StatusOr<std::unique_ptr<AvbHashtreeDescriptor>> verifiedDescriptor =
431 verifyDescriptor(*descriptor);
432 if (!verifiedDescriptor.Ok()) {
433 return StatusOr<ApexVerityData>::MakeError(
434 verifiedDescriptor.ErrorMessage());
435 }
436 verityData.desc = std::move(*verifiedDescriptor);
437
438 // This area is now safe to access, because we just verified it
439 const uint8_t* trailingData =
440 (const uint8_t*)*descriptor + sizeof(AvbHashtreeDescriptor);
441 verityData.salt = getSalt(*verityData.desc, trailingData);
442 verityData.root_digest = getDigest(*verityData.desc, trailingData);
443
444 return StatusOr<ApexVerityData>(std::move(verityData));
445 }
446
VerifyManifestMatches(const std::string & mount_path) const447 Status ApexFile::VerifyManifestMatches(const std::string& mount_path) const {
448 std::string manifest_content;
449 const std::string manifest_path = mount_path + "/" + kManifestFilename;
450
451 if (!android::base::ReadFileToString(manifest_path, &manifest_content)) {
452 std::string err = StringLog()
453 << "Failed to read manifest file: " << manifest_path;
454 return Status::Fail(err);
455 }
456
457 StatusOr<ApexManifest> verifiedManifest = ParseManifest(manifest_content);
458 if (!verifiedManifest.Ok()) {
459 return Status::Fail(verifiedManifest.ErrorMessage());
460 }
461
462 if (!MessageDifferencer::Equals(manifest_, *verifiedManifest)) {
463 return Status::Fail(
464 "Manifest inside filesystem does not match manifest outside it");
465 }
466
467 return Status::Success();
468 }
469
FindApexes(const std::vector<std::string> & paths)470 StatusOr<std::vector<std::string>> FindApexes(
471 const std::vector<std::string>& paths) {
472 using StatusT = StatusOr<std::vector<std::string>>;
473 std::vector<std::string> result;
474 for (const auto& path : paths) {
475 auto exist = PathExists(path);
476 if (!exist.Ok()) {
477 return StatusT::MakeError(exist.ErrorStatus());
478 }
479 if (!*exist) continue;
480
481 const auto& apexes =
482 FindApexFilesByName(path, isPathForBuiltinApexes(path));
483 if (!apexes.Ok()) {
484 return apexes;
485 }
486
487 result.insert(result.end(), apexes->begin(), apexes->end());
488 }
489 return StatusOr<std::vector<std::string>>(result);
490 }
491
FindApexFilesByName(const std::string & path,bool include_dirs)492 StatusOr<std::vector<std::string>> FindApexFilesByName(const std::string& path,
493 bool include_dirs) {
494 auto filter_fn =
495 [include_dirs](const std::filesystem::directory_entry& entry) {
496 std::error_code ec;
497 if (entry.is_regular_file(ec) &&
498 EndsWith(entry.path().filename().string(), kApexPackageSuffix)) {
499 return true; // APEX file, take.
500 }
501 // Directory and asked to scan for flattened.
502 return entry.is_directory(ec) && include_dirs;
503 };
504 return ReadDir(path, filter_fn);
505 }
506
isPathForBuiltinApexes(const std::string & path)507 bool isPathForBuiltinApexes(const std::string& path) {
508 for (const auto& dir : kApexPackageBuiltinDirs) {
509 if (StartsWith(path, dir)) {
510 return true;
511 }
512 }
513 return false;
514 }
515
516 } // namespace apex
517 } // namespace android
518