1 /*
2  * Copyright (C) 2019 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_repository.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/properties.h>
21 #include <android-base/result.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <microdroid/metadata.h>
25 
26 #include <unordered_map>
27 
28 #include "apex_constants.h"
29 #include "apex_file.h"
30 #include "apexd_utils.h"
31 #include "apexd_verity.h"
32 
33 using android::base::EndsWith;
34 using android::base::Error;
35 using android::base::GetProperty;
36 using android::base::Result;
37 
38 namespace android {
39 namespace apex {
40 
ConsumeApexPackageSuffix(const std::string & path)41 std::string ConsumeApexPackageSuffix(const std::string& path) {
42   std::string_view path_view(path);
43   android::base::ConsumeSuffix(&path_view, kApexPackageSuffix);
44   android::base::ConsumeSuffix(&path_view, kCompressedApexPackageSuffix);
45   return std::string(path_view);
46 }
47 
GetApexSelectFilenameFromProp(const std::vector<std::string> & prefixes,const std::string & apex_name)48 std::string GetApexSelectFilenameFromProp(
49     const std::vector<std::string>& prefixes, const std::string& apex_name) {
50   for (const std::string& prefix : prefixes) {
51     const std::string& filename = GetProperty(prefix + apex_name, "");
52     if (filename != "") {
53       return ConsumeApexPackageSuffix(filename);
54     }
55   }
56   return "";
57 }
58 
ScanBuiltInDir(const std::string & dir)59 Result<void> ApexFileRepository::ScanBuiltInDir(const std::string& dir) {
60   LOG(INFO) << "Scanning " << dir << " for pre-installed ApexFiles";
61   if (access(dir.c_str(), F_OK) != 0 && errno == ENOENT) {
62     LOG(WARNING) << dir << " does not exist. Skipping";
63     return {};
64   }
65 
66   Result<std::vector<std::string>> all_apex_files = FindFilesBySuffix(
67       dir, {kApexPackageSuffix, kCompressedApexPackageSuffix});
68   if (!all_apex_files.ok()) {
69     return all_apex_files.error();
70   }
71 
72   // TODO(b/179248390): scan parallelly if possible
73   for (const auto& file : *all_apex_files) {
74     LOG(INFO) << "Found pre-installed APEX " << file;
75     Result<ApexFile> apex_file = ApexFile::Open(file);
76     if (!apex_file.ok()) {
77       return Error() << "Failed to open " << file << " : " << apex_file.error();
78     }
79 
80     const std::string& name = apex_file->GetManifest().name();
81 
82     // Check if this APEX name is treated as a multi-install APEX.
83     //
84     // Note: apexd is a oneshot service which runs at boot, but can be restarted
85     // when needed (such as staging an APEX update). If a multi-install select
86     // property changes between boot and when apexd restarts, the LOG messages
87     // below will report the version that will be activated on next reboot,
88     // which may differ from the currently-active version.
89     std::string select_filename = GetApexSelectFilenameFromProp(
90         multi_install_select_prop_prefixes_, name);
91     if (!select_filename.empty()) {
92       std::string path;
93       if (!android::base::Realpath(apex_file->GetPath(), &path)) {
94         LOG(ERROR) << "Unable to resolve realpath of APEX with path "
95                    << apex_file->GetPath();
96         continue;
97       }
98       if (enforce_multi_install_partition_ &&
99           !android::base::StartsWith(path, "/vendor/apex/")) {
100         LOG(ERROR) << "Multi-install APEX " << path
101                    << " can only be preinstalled on /vendor/apex/.";
102         continue;
103       }
104 
105       auto& keys = multi_install_public_keys_[name];
106       keys.insert(apex_file->GetBundledPublicKey());
107       if (keys.size() > 1) {
108         LOG(ERROR) << "Multi-install APEXes for " << name
109                    << " have different public keys.";
110         // If any versions of a multi-installed APEX differ in public key,
111         // then no version should be installed.
112         if (auto it = pre_installed_store_.find(name);
113             it != pre_installed_store_.end()) {
114           pre_installed_store_.erase(it);
115         }
116         continue;
117       }
118 
119       if (ConsumeApexPackageSuffix(android::base::Basename(path)) ==
120           select_filename) {
121         LOG(INFO) << "Found APEX at path " << path << " for multi-install APEX "
122                   << name;
123         // Add the APEX file to the store if its filename matches the property.
124         pre_installed_store_.emplace(name, std::move(*apex_file));
125       } else {
126         LOG(INFO) << "Skipping APEX at path " << path
127                   << " because it does not match expected multi-install"
128                   << " APEX property for " << name;
129       }
130 
131       continue;
132     }
133 
134     auto it = pre_installed_store_.find(name);
135     if (it == pre_installed_store_.end()) {
136       pre_installed_store_.emplace(name, std::move(*apex_file));
137     } else if (it->second.GetPath() != apex_file->GetPath()) {
138       auto level = base::FATAL;
139       if (ignore_duplicate_apex_definitions_) {
140         level = base::INFO;
141       }
142       // On some development (non-REL) builds the VNDK apex could be in /vendor.
143       // When testing CTS-on-GSI on these builds, there would be two VNDK apexes
144       // in the system, one in /system and one in /vendor.
145       static constexpr char kVndkApexModuleNamePrefix[] = "com.android.vndk.";
146       static constexpr char kPlatformVersionCodenameProperty[] =
147           "ro.build.version.codename";
148       if (android::base::StartsWith(name, kVndkApexModuleNamePrefix) &&
149           GetProperty(kPlatformVersionCodenameProperty, "REL") != "REL") {
150         level = android::base::INFO;
151       }
152       LOG(level) << "Found two apex packages " << it->second.GetPath()
153                  << " and " << apex_file->GetPath()
154                  << " with the same module name " << name;
155     } else if (it->second.GetBundledPublicKey() !=
156                apex_file->GetBundledPublicKey()) {
157       LOG(FATAL) << "Public key of apex package " << it->second.GetPath()
158                  << " (" << name << ") has unexpectedly changed";
159     }
160   }
161   multi_install_public_keys_.clear();
162   return {};
163 }
164 
GetInstance()165 ApexFileRepository& ApexFileRepository::GetInstance() {
166   static ApexFileRepository instance;
167   return instance;
168 }
169 
AddPreInstalledApex(const std::vector<std::string> & prebuilt_dirs)170 android::base::Result<void> ApexFileRepository::AddPreInstalledApex(
171     const std::vector<std::string>& prebuilt_dirs) {
172   for (const auto& dir : prebuilt_dirs) {
173     if (auto result = ScanBuiltInDir(dir); !result.ok()) {
174       return result.error();
175     }
176   }
177   return {};
178 }
179 
AddBlockApex(const std::string & metadata_partition)180 Result<int> ApexFileRepository::AddBlockApex(
181     const std::string& metadata_partition) {
182   CHECK(!block_disk_path_.has_value())
183       << "AddBlockApex() can't be called twice.";
184 
185   auto metadata_ready = WaitForFile(metadata_partition, kBlockApexWaitTime);
186   if (!metadata_ready.ok()) {
187     LOG(ERROR) << "Error waiting for metadata_partition : "
188                << metadata_ready.error();
189     return {};
190   }
191 
192   // TODO(b/185069443) consider moving the logic to find disk_path from
193   // metadata_partition to its own library
194   LOG(INFO) << "Scanning " << metadata_partition << " for host apexes";
195   if (access(metadata_partition.c_str(), F_OK) != 0 && errno == ENOENT) {
196     LOG(WARNING) << metadata_partition << " does not exist. Skipping";
197     return {};
198   }
199 
200   std::string metadata_realpath;
201   if (!android::base::Realpath(metadata_partition, &metadata_realpath)) {
202     LOG(WARNING) << "Can't get realpath of " << metadata_partition
203                  << ". Skipping";
204     return {};
205   }
206 
207   std::string_view metadata_path_view(metadata_realpath);
208   if (!android::base::ConsumeSuffix(&metadata_path_view, "1")) {
209     LOG(WARNING) << metadata_realpath << " is not a first partition. Skipping";
210     return {};
211   }
212 
213   block_disk_path_ = std::string(metadata_path_view);
214 
215   // Read the payload metadata.
216   // "metadata" can be overridden by microdroid_manager. To ensure that
217   // "microdroid" is started with the same/unmodified set of host APEXes,
218   // microdroid stores APEXes' pubkeys in its encrypted instance disk. Next
219   // time, microdroid checks if there's pubkeys in the instance disk and use
220   // them to activate APEXes. Microdroid_manager passes pubkeys in instance.img
221   // via the following file.
222   if (auto exists = PathExists("/apex/vm-payload-metadata");
223       exists.ok() && *exists) {
224     metadata_realpath = "/apex/vm-payload-metadata";
225     LOG(INFO) << "Overriding metadata to " << metadata_realpath;
226   }
227   auto metadata = android::microdroid::ReadMetadata(metadata_realpath);
228   if (!metadata.ok()) {
229     LOG(WARNING) << "Failed to load metadata from " << metadata_realpath
230                  << ". Skipping: " << metadata.error();
231     return {};
232   }
233 
234   int ret = 0;
235 
236   // subsequent partitions are APEX archives.
237   static constexpr const int kFirstApexPartition = 2;
238   for (int i = 0; i < metadata->apexes_size(); i++) {
239     const auto& apex_config = metadata->apexes(i);
240 
241     const std::string apex_path =
242         *block_disk_path_ + std::to_string(i + kFirstApexPartition);
243 
244     auto apex_ready = WaitForFile(apex_path, kBlockApexWaitTime);
245     if (!apex_ready.ok()) {
246       return Error() << "Error waiting for apex file : " << apex_ready.error();
247     }
248 
249     auto apex_file = ApexFile::Open(apex_path);
250     if (!apex_file.ok()) {
251       return Error() << "Failed to open " << apex_path << " : "
252                      << apex_file.error();
253     }
254 
255     // When metadata specifies the public key of the apex, it should match the
256     // bundled key. Otherwise we accept it.
257     if (apex_config.public_key() != "" &&
258         apex_config.public_key() != apex_file->GetBundledPublicKey()) {
259       return Error() << "public key doesn't match: " << apex_path;
260     }
261 
262     const std::string& name = apex_file->GetManifest().name();
263 
264     // When metadata specifies the manifest name and version of the apex, it
265     // should match what we see in the manifest.
266     if (apex_config.manifest_name() != "" &&
267         apex_config.manifest_name() != name) {
268       return Error() << "manifest name doesn't match: " << apex_path;
269     }
270 
271     if (apex_config.manifest_version() != 0 &&
272         apex_config.manifest_version() != apex_file->GetManifest().version()) {
273       return Error() << "manifest version doesn't match: " << apex_path;
274     }
275 
276     BlockApexOverride overrides;
277 
278     // A block device doesn't have an inherent timestamp, so it is carried in
279     // the metadata.
280     if (int64_t last_update_seconds = apex_config.last_update_seconds();
281         last_update_seconds != 0) {
282       overrides.last_update_seconds = last_update_seconds;
283     }
284 
285     // When metadata specifies the root digest of the apex, it should be used
286     // when activating the apex. So we need to keep it.
287     if (auto root_digest = apex_config.root_digest(); root_digest != "") {
288       overrides.block_apex_root_digest =
289           BytesToHex(reinterpret_cast<const uint8_t*>(root_digest.data()),
290                      root_digest.size());
291     }
292 
293     if (overrides.last_update_seconds.has_value() ||
294         overrides.block_apex_root_digest.has_value()) {
295       block_apex_overrides_.emplace(apex_path, std::move(overrides));
296     }
297 
298     // Depending on whether the APEX was a factory version in the host or not,
299     // put it to different stores.
300     auto& store = apex_config.is_factory() ? pre_installed_store_ : data_store_;
301     // We want "uniqueness" in each store.
302     if (auto it = store.find(name); it != store.end()) {
303       return Error() << "duplicate of " << name << " found in "
304                      << it->second.GetPath();
305     }
306     store.emplace(name, std::move(*apex_file));
307 
308     ret++;
309   }
310   return {ret};
311 }
312 
313 // TODO(b/179497746): AddDataApex should not concern with filtering out invalid
314 //   apex.
AddDataApex(const std::string & data_dir)315 Result<void> ApexFileRepository::AddDataApex(const std::string& data_dir) {
316   LOG(INFO) << "Scanning " << data_dir << " for data ApexFiles";
317   if (access(data_dir.c_str(), F_OK) != 0 && errno == ENOENT) {
318     LOG(WARNING) << data_dir << " does not exist. Skipping";
319     return {};
320   }
321 
322   Result<std::vector<std::string>> active_apex =
323       FindFilesBySuffix(data_dir, {kApexPackageSuffix});
324   if (!active_apex.ok()) {
325     return active_apex.error();
326   }
327 
328   // TODO(b/179248390): scan parallelly if possible
329   for (const auto& file : *active_apex) {
330     LOG(INFO) << "Found updated apex " << file;
331     Result<ApexFile> apex_file = ApexFile::Open(file);
332     if (!apex_file.ok()) {
333       LOG(ERROR) << "Failed to open " << file << " : " << apex_file.error();
334       continue;
335     }
336 
337     const std::string& name = apex_file->GetManifest().name();
338     if (!HasPreInstalledVersion(name)) {
339       LOG(ERROR) << "Skipping " << file << " : no preinstalled apex";
340       // Ignore data apex without corresponding pre-installed apex
341       continue;
342     }
343 
344     std::string select_filename = GetApexSelectFilenameFromProp(
345         multi_install_select_prop_prefixes_, name);
346     if (!select_filename.empty()) {
347       LOG(WARNING) << "APEX " << name << " is a multi-installed APEX."
348                    << " Any updated version in /data will always overwrite"
349                    << " the multi-installed preinstalled version, if possible.";
350     }
351 
352     auto pre_installed_public_key = GetPublicKey(name);
353     if (!pre_installed_public_key.ok() ||
354         apex_file->GetBundledPublicKey() != *pre_installed_public_key) {
355       // Ignore data apex if public key doesn't match with pre-installed apex
356       LOG(ERROR) << "Skipping " << file
357                  << " : public key doesn't match pre-installed one";
358       continue;
359     }
360 
361     if (EndsWith(apex_file->GetPath(), kDecompressedApexPackageSuffix)) {
362       LOG(WARNING) << "Skipping " << file
363                    << " : Non-decompressed APEX should not have "
364                    << kDecompressedApexPackageSuffix << " suffix";
365       continue;
366     }
367 
368     auto it = data_store_.find(name);
369     if (it == data_store_.end()) {
370       data_store_.emplace(name, std::move(*apex_file));
371       continue;
372     }
373 
374     const auto& existing_version = it->second.GetManifest().version();
375     const auto new_version = apex_file->GetManifest().version();
376     // If multiple data apexs are preset, select the one with highest version
377     bool prioritize_higher_version = new_version > existing_version;
378     // For same version, non-decompressed apex gets priority
379     if (prioritize_higher_version) {
380       it->second = std::move(*apex_file);
381     }
382   }
383   return {};
384 }
385 
386 // TODO(b/179497746): remove this method when we add api for fetching ApexFile
387 //  by name
GetPublicKey(const std::string & name) const388 Result<const std::string> ApexFileRepository::GetPublicKey(
389     const std::string& name) const {
390   auto it = pre_installed_store_.find(name);
391   if (it == pre_installed_store_.end()) {
392     // Special casing for APEXes backed by block devices, i.e. APEXes in VM.
393     // Inside a VM, we fall back to find the key from data_store_. This is
394     // because an APEX is put to either pre_installed_store_ or data_store,
395     // depending on whether it was a factory APEX or not in the host.
396     it = data_store_.find(name);
397     if (it != data_store_.end() && IsBlockApex(it->second)) {
398       return it->second.GetBundledPublicKey();
399     }
400     return Error() << "No preinstalled apex found for package " << name;
401   }
402   return it->second.GetBundledPublicKey();
403 }
404 
405 // TODO(b/179497746): remove this method when we add api for fetching ApexFile
406 //  by name
GetPreinstalledPath(const std::string & name) const407 Result<const std::string> ApexFileRepository::GetPreinstalledPath(
408     const std::string& name) const {
409   auto it = pre_installed_store_.find(name);
410   if (it == pre_installed_store_.end()) {
411     return Error() << "No preinstalled data found for package " << name;
412   }
413   return it->second.GetPath();
414 }
415 
416 // TODO(b/179497746): remove this method when we add api for fetching ApexFile
417 //  by name
GetDataPath(const std::string & name) const418 Result<const std::string> ApexFileRepository::GetDataPath(
419     const std::string& name) const {
420   auto it = data_store_.find(name);
421   if (it == data_store_.end()) {
422     return Error() << "No data apex found for package " << name;
423   }
424   return it->second.GetPath();
425 }
426 
GetBlockApexRootDigest(const std::string & path) const427 std::optional<std::string> ApexFileRepository::GetBlockApexRootDigest(
428     const std::string& path) const {
429   auto it = block_apex_overrides_.find(path);
430   if (it == block_apex_overrides_.end()) {
431     return std::nullopt;
432   }
433   return it->second.block_apex_root_digest;
434 }
435 
GetBlockApexLastUpdateSeconds(const std::string & path) const436 std::optional<int64_t> ApexFileRepository::GetBlockApexLastUpdateSeconds(
437     const std::string& path) const {
438   auto it = block_apex_overrides_.find(path);
439   if (it == block_apex_overrides_.end()) {
440     return std::nullopt;
441   }
442   return it->second.last_update_seconds;
443 }
444 
HasPreInstalledVersion(const std::string & name) const445 bool ApexFileRepository::HasPreInstalledVersion(const std::string& name) const {
446   return pre_installed_store_.find(name) != pre_installed_store_.end();
447 }
448 
HasDataVersion(const std::string & name) const449 bool ApexFileRepository::HasDataVersion(const std::string& name) const {
450   return data_store_.find(name) != data_store_.end();
451 }
452 
453 // ApexFile is considered a decompressed APEX if it is located in decompression
454 // dir
IsDecompressedApex(const ApexFile & apex) const455 bool ApexFileRepository::IsDecompressedApex(const ApexFile& apex) const {
456   return apex.GetPath().starts_with(decompression_dir_);
457 }
458 
IsPreInstalledApex(const ApexFile & apex) const459 bool ApexFileRepository::IsPreInstalledApex(const ApexFile& apex) const {
460   auto it = pre_installed_store_.find(apex.GetManifest().name());
461   if (it == pre_installed_store_.end()) {
462     return false;
463   }
464   return it->second.GetPath() == apex.GetPath() || IsDecompressedApex(apex);
465 }
466 
IsBlockApex(const ApexFile & apex) const467 bool ApexFileRepository::IsBlockApex(const ApexFile& apex) const {
468   return block_disk_path_.has_value() &&
469          apex.GetPath().starts_with(*block_disk_path_);
470 }
471 
GetPreInstalledApexFiles() const472 std::vector<ApexFileRef> ApexFileRepository::GetPreInstalledApexFiles() const {
473   std::vector<ApexFileRef> result;
474   result.reserve(pre_installed_store_.size());
475   for (const auto& it : pre_installed_store_) {
476     result.emplace_back(std::cref(it.second));
477   }
478   return std::move(result);
479 }
480 
GetDataApexFiles() const481 std::vector<ApexFileRef> ApexFileRepository::GetDataApexFiles() const {
482   std::vector<ApexFileRef> result;
483   result.reserve(data_store_.size());
484   for (const auto& it : data_store_) {
485     result.emplace_back(std::cref(it.second));
486   }
487   return std::move(result);
488 }
489 
490 // Group pre-installed APEX and data APEX by name
491 std::unordered_map<std::string, std::vector<ApexFileRef>>
AllApexFilesByName() const492 ApexFileRepository::AllApexFilesByName() const {
493   // Collect all apex files
494   std::vector<ApexFileRef> all_apex_files;
495   auto pre_installed_apexs = GetPreInstalledApexFiles();
496   auto data_apexs = GetDataApexFiles();
497   std::move(pre_installed_apexs.begin(), pre_installed_apexs.end(),
498             std::back_inserter(all_apex_files));
499   std::move(data_apexs.begin(), data_apexs.end(),
500             std::back_inserter(all_apex_files));
501 
502   // Group them by name
503   std::unordered_map<std::string, std::vector<ApexFileRef>> result;
504   for (const auto& apex_file_ref : all_apex_files) {
505     const ApexFile& apex_file = apex_file_ref.get();
506     const std::string& package_name = apex_file.GetManifest().name();
507     if (result.find(package_name) == result.end()) {
508       result[package_name] = std::vector<ApexFileRef>{};
509     }
510     result[package_name].emplace_back(apex_file_ref);
511   }
512 
513   return std::move(result);
514 }
515 
GetDataApex(const std::string & name) const516 ApexFileRef ApexFileRepository::GetDataApex(const std::string& name) const {
517   auto it = data_store_.find(name);
518   CHECK(it != data_store_.end());
519   return std::cref(it->second);
520 }
521 
GetPreInstalledApex(const std::string & name) const522 ApexFileRef ApexFileRepository::GetPreInstalledApex(
523     const std::string& name) const {
524   auto it = pre_installed_store_.find(name);
525   CHECK(it != pre_installed_store_.end());
526   return std::cref(it->second);
527 }
528 
529 }  // namespace apex
530 }  // namespace android
531