• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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