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