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 #pragma once 18 19 #include <android-base/result.h> 20 21 #include <functional> 22 #include <optional> 23 #include <string> 24 #include <unordered_map> 25 #include <unordered_set> 26 #include <vector> 27 28 #include "apex_constants.h" 29 #include "apex_file.h" 30 31 namespace android::apex { 32 33 using ApexFileRef = std::reference_wrapper<const android::apex::ApexFile>; 34 35 // This class serves as a ApexFile repository for all apexes on device. It also 36 // provides information about the ApexFiles it hosts, such as which are 37 // pre-installed and which are data. Such information can be used, for example, 38 // to verify validity of an apex before trying to mount it. 39 // 40 // It's expected to have a single instance of this class in a process that 41 // mounts apexes (e.g. apexd, otapreopt_chroot). 42 class ApexFileRepository final { 43 public: 44 // c-tors and d-tor are exposed for testing. 45 explicit ApexFileRepository( 46 const std::string& decompression_dir = kApexDecompressedDir) decompression_dir_(decompression_dir)47 : decompression_dir_(decompression_dir) {} ApexFileRepository(bool enforce_multi_install_partition,const std::vector<std::string> & multi_install_select_prop_prefixes)48 explicit ApexFileRepository( 49 bool enforce_multi_install_partition, 50 const std::vector<std::string>& multi_install_select_prop_prefixes) 51 : multi_install_select_prop_prefixes_(multi_install_select_prop_prefixes), 52 enforce_multi_install_partition_(enforce_multi_install_partition) {} 53 54 // Returns a singletone instance of this class. 55 static ApexFileRepository& GetInstance(); 56 57 // Populate instance by collecting pre-installed apex files from the given 58 // |partition_to_prebuilt_dirs|. 59 // Note: this call is **not thread safe** and is expected to be performed in a 60 // single thread during initialization of apexd. After initialization is 61 // finished, all queries to the instance are thread safe. 62 android::base::Result<void> AddPreInstalledApex( 63 const std::unordered_map<ApexPartition, std::string>& 64 partition_to_prebuilt_dirs); 65 66 // Populate instance by collecting host-provided apex files via 67 // |metadata_partition|. Host can provide its apexes to a VM instance via the 68 // virtual disk image which has partitions: (see 69 // /packages/modules/Virtualization/microdroid for the details) 70 // - metadata partition(/dev/block/vd*1) should be accessed by 71 // setting the system property apexd.payload_metadata.prop. On microdroid, 72 // this is /dev/block/by-name/payload-metadata. 73 // - each subsequence partition(/dev/block/vd*{2,3,..}) represents an APEX 74 // archive. 75 // It will fail if there is more than one apex with the same name in 76 // pre-installed and block apexes. Note: this call is **not thread safe** and 77 // is expected to be performed in a single thread during initialization of 78 // apexd. After initialization is finished, all queries to the instance are 79 // thread safe. 80 // This will return the number of block apexes that were added. 81 android::base::Result<int> AddBlockApex( 82 const std::string& metadata_partition); 83 84 // Populate instance by collecting data apex files from the given |data_dir|. 85 // Note: this call is **not thread safe** and is expected to be performed in a 86 // single thread during initialization of apexd. After initialization is 87 // finished, all queries to the instance are thread safe. 88 android::base::Result<void> AddDataApex(const std::string& data_dir); 89 90 // Populates instance by collecting pre-installed credential files (.avbpubkey 91 // for now) and blocklist files from the given directories. They are needed 92 // specifically for brand-new APEX. 93 // Note: this call is **not thread safe** and 94 // is expected to be performed in a single thread during initialization of 95 // apexd. After initialization is finished, all queries to the instance are 96 // thread safe. 97 android::base::Result<void> AddBrandNewApexCredentialAndBlocklist( 98 const std::unordered_map<ApexPartition, std::string>& 99 partition_to_dir_map); 100 101 // Returns the mapping partition of a specific apex. 102 // For pre-installed APEX, it is the partition where the pre-installed package 103 // resides. For brand-new APEX, it is the partition where the 104 // credentials to verify the package reside. 105 android::base::Result<ApexPartition> GetPartition(const ApexFile& apex) const; 106 107 // Returns trusted public key for an apex with the given |name|. 108 android::base::Result<const std::string> GetPublicKey( 109 const std::string& name) const; 110 111 // Returns path to the pre-installed version of an apex with the given |name|. 112 // For brand-new APEX, returns Error. 113 // For block APEX which is not set as factory, returns Error. 114 android::base::Result<const std::string> GetPreinstalledPath( 115 const std::string& name) const; 116 117 // Returns path to the data version of an apex with the given |name|. 118 android::base::Result<const std::string> GetDataPath( 119 const std::string& name) const; 120 121 // Returns root digest of an apex with the given |path| for block apexes. 122 std::optional<std::string> GetBlockApexRootDigest( 123 const std::string& path) const; 124 125 // Returns timestamp to be used for the block apex of the given |path|. 126 std::optional<int64_t> GetBlockApexLastUpdateSeconds( 127 const std::string& path) const; 128 129 // Checks whether there is a pre-installed version of an apex with the given 130 // |name|. 131 bool HasPreInstalledVersion(const std::string& name) const; 132 133 // Checks whether there is a data version of an apex with the given |name|. 134 bool HasDataVersion(const std::string& name) const; 135 136 // Checks if given |apex| is pre-installed. 137 bool IsPreInstalledApex(const ApexFile& apex) const; 138 139 // Checks if given |apex| is decompressed from a pre-installed APEX 140 bool IsDecompressedApex(const ApexFile& apex) const; 141 142 // Checks if given |apex| is loaded from block device. 143 bool IsBlockApex(const ApexFile& apex) const; 144 145 // Returns reference to all pre-installed APEX on device 146 std::vector<ApexFileRef> GetPreInstalledApexFiles() const; 147 148 // Returns reference to all data APEX on device 149 std::vector<ApexFileRef> GetDataApexFiles() const; 150 151 // Returns the partition of the pre-installed public key which exactly matches 152 // the |public_key|. 153 std::optional<ApexPartition> GetBrandNewApexPublicKeyPartition( 154 const std::string& public_key) const; 155 156 // Returns the blocked version number of a specific brand-new APEX in a 157 // specific partition. The brand-new APEX is only allowed when its version is 158 // larger than the blocked version. 159 // Returns |std::nullopt| if the |apex_name| is not configured in blocklist. 160 std::optional<int64_t> GetBrandNewApexBlockedVersion( 161 ApexPartition partition, const std::string& apex_name) const; 162 163 // Group all ApexFiles on device by their package name 164 std::unordered_map<std::string, std::vector<ApexFileRef>> AllApexFilesByName() 165 const; 166 167 // Returns a pre-installed version of apex with the given name. Caller is 168 // expected to check if there is a pre-installed apex with the given name 169 // using |HasPreinstalledVersion| function. 170 ApexFileRef GetPreInstalledApex(const std::string& name) const; 171 // Returns a data version of apex with the given name. Caller is 172 // expected to check if there is a data apex with the given name 173 // using |HasDataVersion| function. 174 ApexFileRef GetDataApex(const std::string& name) const; 175 176 // Returns if installation of brand-new APEX is enabled. IsBrandNewApexEnabled()177 static inline bool IsBrandNewApexEnabled() { return enable_brand_new_apex_; }; 178 179 // Enables installation of brand-new APEX. EnableBrandNewApex()180 static inline void EnableBrandNewApex() { enable_brand_new_apex_ = true; }; 181 182 // Clears ApexFileRepostiry. 183 // Only use in tests. 184 void Reset(const std::string& decompression_dir = kApexDecompressedDir) { 185 pre_installed_store_.clear(); 186 data_store_.clear(); 187 partition_store_.clear(); 188 brand_new_apex_blocked_version_.clear(); 189 brand_new_apex_pubkeys_.clear(); 190 block_apex_overrides_.clear(); 191 decompression_dir_ = decompression_dir; 192 block_disk_path_.reset(); 193 enable_brand_new_apex_ = false; 194 } 195 196 private: 197 // Non-copyable && non-moveable. 198 ApexFileRepository(const ApexFileRepository&) = delete; 199 ApexFileRepository& operator=(const ApexFileRepository&) = delete; 200 ApexFileRepository& operator=(ApexFileRepository&&) = delete; 201 ApexFileRepository(ApexFileRepository&&) = delete; 202 203 // Scans apexes in the given directory and adds collected data into 204 // |pre_installed_store_| and |partition_store_|. 205 android::base::Result<void> ScanBuiltInDir(const std::string& dir, 206 ApexPartition partition); 207 208 std::unordered_map<std::string, ApexFile> pre_installed_store_, data_store_; 209 210 // Map from APEX name to their partition. For pre-installed APEX, this is the 211 // partition where it is pre-installed. For brand-new APEX, this is the 212 // partition where its credential is pre-installed. 213 std::unordered_map<std::string, ApexPartition> partition_store_; 214 215 // Blocked versions for brand-new APEX mapped by their holding partition. 216 std::unordered_map<ApexPartition, std::unordered_map<std::string, int64_t>> 217 brand_new_apex_blocked_version_; 218 219 // Map from trusted public keys for brand-new APEX to their holding partition. 220 std::unordered_map<std::string, ApexPartition> brand_new_apex_pubkeys_; 221 222 // Multi-installed APEX name -> all encountered public keys for this APEX. 223 std::unordered_map<std::string, std::unordered_set<std::string>> 224 multi_install_public_keys_; 225 226 // Prefixes used when looking for multi-installed APEX sysprops. 227 // Order matters: the first non-empty prop value is returned. 228 std::vector<std::string> multi_install_select_prop_prefixes_ = 229 kMultiApexSelectPrefix; 230 231 // Allows multi-install APEXes outside of expected partitions. 232 // Only set false in tests. 233 bool enforce_multi_install_partition_ = true; 234 235 // Disallows installation of brand-new APEX by default. 236 inline static bool enable_brand_new_apex_ = false; 237 238 // Decompression directory which will be used to determine if apex is 239 // decompressed or not 240 std::string decompression_dir_; 241 242 // Disk path where block apexes are read from. AddBlockApex() sets this. 243 std::optional<std::string> block_disk_path_; 244 245 // Information from the metadata for block apexes, overriding the file data. 246 struct BlockApexOverride { 247 // Root digest for the APEX. When specified in block apex config, it 248 // should be used/checked when activating the apex to avoid 249 // TOCTOU(time-of-check to time-of-use). 250 std::optional<std::string> block_apex_root_digest; 251 // The last update time of the APEX. 252 std::optional<int64_t> last_update_seconds; 253 }; 254 255 // Use "path" as key instead of APEX name because there can be multiple 256 // versions of sharedlibs APEXes. 257 std::unordered_map<std::string, BlockApexOverride> block_apex_overrides_; 258 }; 259 260 } // namespace android::apex 261