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_constants.h"
36 #include "apex_preinstalled_data.h"
37 #include "apexd_utils.h"
38 #include "string_log.h"
39 
40 using android::base::EndsWith;
41 using android::base::Error;
42 using android::base::ReadFullyAtOffset;
43 using android::base::Result;
44 using android::base::StartsWith;
45 using android::base::unique_fd;
46 using google::protobuf::util::MessageDifferencer;
47 
48 namespace android {
49 namespace apex {
50 namespace {
51 
52 constexpr const char* kImageFilename = "apex_payload.img";
53 constexpr const char* kBundledPublicKeyFilename = "apex_pubkey";
54 
55 }  // namespace
56 
Open(const std::string & path)57 Result<ApexFile> ApexFile::Open(const std::string& path) {
58   int32_t image_offset;
59   size_t image_size;
60   std::string manifest_content;
61   std::string pubkey;
62 
63   ZipArchiveHandle handle;
64   auto handle_guard =
65       android::base::make_scope_guard([&handle] { CloseArchive(handle); });
66   int ret = OpenArchive(path.c_str(), &handle);
67   if (ret < 0) {
68     return Error() << "Failed to open package " << path << ": "
69                    << ErrorCodeString(ret);
70   }
71 
72   // Locate the mountable image within the zipfile and store offset and size.
73   ZipEntry entry;
74   ret = FindEntry(handle, kImageFilename, &entry);
75   if (ret < 0) {
76     return Error() << "Could not find entry \"" << kImageFilename
77                    << "\" in package " << path << ": " << ErrorCodeString(ret);
78   }
79   image_offset = entry.offset;
80   image_size = entry.uncompressed_length;
81 
82   ret = FindEntry(handle, kManifestFilenamePb, &entry);
83   if (ret < 0) {
84     return Error() << "Could not find entry \"" << kManifestFilenamePb
85                    << "\" in package " << path << ": " << ErrorCodeString(ret);
86   }
87 
88   uint32_t length = entry.uncompressed_length;
89   manifest_content.resize(length, '\0');
90   ret = ExtractToMemory(handle, &entry,
91                         reinterpret_cast<uint8_t*>(&(manifest_content)[0]),
92                         length);
93   if (ret != 0) {
94     return Error() << "Failed to extract manifest from package " << path << ": "
95                    << ErrorCodeString(ret);
96   }
97 
98   ret = FindEntry(handle, kBundledPublicKeyFilename, &entry);
99   if (ret >= 0) {
100     length = entry.uncompressed_length;
101     pubkey.resize(length, '\0');
102     ret = ExtractToMemory(handle, &entry,
103                           reinterpret_cast<uint8_t*>(&(pubkey)[0]), length);
104     if (ret != 0) {
105       return Error() << "Failed to extract public key from package " << path
106                      << ": " << ErrorCodeString(ret);
107     }
108   }
109 
110   Result<ApexManifest> manifest = ParseManifest(manifest_content);
111   if (!manifest.ok()) {
112     return manifest.error();
113   }
114 
115   return ApexFile(path, image_offset, image_size, std::move(*manifest), pubkey,
116                   isPathForBuiltinApexes(path));
117 }
118 
119 // AVB-related code.
120 
121 namespace {
122 
123 static constexpr const char* kApexKeyProp = "apex.key";
124 
125 static constexpr int kVbMetaMaxSize = 64 * 1024;
126 
bytes_to_hex(const uint8_t * bytes,size_t bytes_len)127 std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
128   std::ostringstream s;
129 
130   s << std::hex << std::setfill('0');
131   for (size_t i = 0; i < bytes_len; i++) {
132     s << std::setw(2) << static_cast<int>(bytes[i]);
133   }
134   return s.str();
135 }
136 
getSalt(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)137 std::string getSalt(const AvbHashtreeDescriptor& desc,
138                     const uint8_t* trailingData) {
139   const uint8_t* desc_salt = trailingData + desc.partition_name_len;
140 
141   return bytes_to_hex(desc_salt, desc.salt_len);
142 }
143 
getDigest(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)144 std::string getDigest(const AvbHashtreeDescriptor& desc,
145                       const uint8_t* trailingData) {
146   const uint8_t* desc_digest =
147       trailingData + desc.partition_name_len + desc.salt_len;
148 
149   return bytes_to_hex(desc_digest, desc.root_digest_len);
150 }
151 
getAvbFooter(const ApexFile & apex,const unique_fd & fd)152 Result<std::unique_ptr<AvbFooter>> getAvbFooter(const ApexFile& apex,
153                                                 const unique_fd& fd) {
154   std::array<uint8_t, AVB_FOOTER_SIZE> footer_data;
155   auto footer = std::make_unique<AvbFooter>();
156 
157   // The AVB footer is located in the last part of the image
158   off_t offset = apex.GetImageSize() + apex.GetImageOffset() - AVB_FOOTER_SIZE;
159   int ret = lseek(fd, offset, SEEK_SET);
160   if (ret == -1) {
161     return ErrnoError() << "Couldn't seek to AVB footer";
162   }
163 
164   ret = read(fd, footer_data.data(), AVB_FOOTER_SIZE);
165   if (ret != AVB_FOOTER_SIZE) {
166     return ErrnoError() << "Couldn't read AVB footer";
167   }
168 
169   if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_data.data(),
170                                         footer.get())) {
171     return Error() << "AVB footer verification failed.";
172   }
173 
174   LOG(VERBOSE) << "AVB footer verification successful.";
175   return footer;
176 }
177 
CompareKeys(const uint8_t * key,size_t length,const std::string & public_key_content)178 bool CompareKeys(const uint8_t* key, size_t length,
179                  const std::string& public_key_content) {
180   return public_key_content.length() == length &&
181          memcmp(&public_key_content[0], key, length) == 0;
182 }
183 
getPublicKeyName(const ApexFile & apex,const uint8_t * data,size_t length)184 Result<std::string> getPublicKeyName(const ApexFile& apex, const uint8_t* data,
185                                      size_t length) {
186   size_t keyNameLen;
187   const char* keyName = avb_property_lookup(data, length, kApexKeyProp,
188                                             strlen(kApexKeyProp), &keyNameLen);
189   if (keyName == nullptr || keyNameLen == 0) {
190     return Error() << "Cannot find prop '" << kApexKeyProp << "' from "
191                    << apex.GetPath();
192   }
193 
194   if (keyName != apex.GetManifest().name()) {
195     return Error() << "Key mismatch: apex name is '"
196                    << apex.GetManifest().name() << "'"
197                    << " but key name is '" << keyName << "'";
198   }
199   return keyName;
200 }
201 
verifyVbMetaSignature(const ApexFile & apex,const uint8_t * data,size_t length)202 Result<void> verifyVbMetaSignature(const ApexFile& apex, const uint8_t* data,
203                                    size_t length) {
204   const uint8_t* pk;
205   size_t pk_len;
206   AvbVBMetaVerifyResult res;
207 
208   res = avb_vbmeta_image_verify(data, length, &pk, &pk_len);
209   switch (res) {
210     case AVB_VBMETA_VERIFY_RESULT_OK:
211       break;
212     case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
213     case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
214     case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
215       return Error() << "Error verifying " << apex.GetPath() << ": "
216                      << avb_vbmeta_verify_result_to_string(res);
217     case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
218       return Error() << "Error verifying " << apex.GetPath() << ": "
219                      << "invalid vbmeta header";
220     case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
221       return Error() << "Error verifying " << apex.GetPath() << ": "
222                      << "unsupported version";
223     default:
224       return Errorf("Unknown vmbeta_image_verify return value");
225   }
226 
227   Result<std::string> key_name = getPublicKeyName(apex, data, length);
228   if (!key_name.ok()) {
229     return key_name.error();
230   }
231 
232   Result<const std::string> public_key = getApexKey(*key_name);
233   if (public_key.ok()) {
234     // TODO(b/115718846)
235     // We need to decide whether we need rollback protection, and whether
236     // we can use the rollback protection provided by libavb.
237     if (!CompareKeys(pk, pk_len, *public_key)) {
238       return Error() << "Error verifying " << apex.GetPath() << ": "
239                      << "public key doesn't match the pre-installed one";
240     }
241   } else {
242     return public_key.error();
243   }
244   LOG(VERBOSE) << apex.GetPath() << ": public key matches.";
245   return {};
246 }
247 
verifyVbMeta(const ApexFile & apex,const unique_fd & fd,const AvbFooter & footer)248 Result<std::unique_ptr<uint8_t[]>> verifyVbMeta(const ApexFile& apex,
249                                                 const unique_fd& fd,
250                                                 const AvbFooter& footer) {
251   if (footer.vbmeta_size > kVbMetaMaxSize) {
252     return Errorf("VbMeta size in footer exceeds kVbMetaMaxSize.");
253   }
254 
255   off_t offset = apex.GetImageOffset() + footer.vbmeta_offset;
256   std::unique_ptr<uint8_t[]> vbmeta_buf(new uint8_t[footer.vbmeta_size]);
257 
258   if (!ReadFullyAtOffset(fd, vbmeta_buf.get(), footer.vbmeta_size, offset)) {
259     return ErrnoError() << "Couldn't read AVB meta-data";
260   }
261 
262   Result<void> st =
263       verifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size);
264   if (!st.ok()) {
265     return st.error();
266   }
267 
268   return vbmeta_buf;
269 }
270 
findDescriptor(uint8_t * vbmeta_data,size_t vbmeta_size)271 Result<const AvbHashtreeDescriptor*> findDescriptor(uint8_t* vbmeta_data,
272                                                     size_t vbmeta_size) {
273   const AvbDescriptor** descriptors;
274   size_t num_descriptors;
275 
276   descriptors =
277       avb_descriptor_get_all(vbmeta_data, vbmeta_size, &num_descriptors);
278 
279   // avb_descriptor_get_all() returns an internally allocated array
280   // of pointers and it needs to be avb_free()ed after using it.
281   auto guard = android::base::ScopeGuard(std::bind(avb_free, descriptors));
282 
283   for (size_t i = 0; i < num_descriptors; i++) {
284     AvbDescriptor desc;
285     if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
286       return Errorf("Couldn't validate AvbDescriptor.");
287     }
288 
289     if (desc.tag != AVB_DESCRIPTOR_TAG_HASHTREE) {
290       // Ignore other descriptors
291       continue;
292     }
293 
294     return (const AvbHashtreeDescriptor*)descriptors[i];
295   }
296 
297   return Errorf("Couldn't find any AVB hashtree descriptors.");
298 }
299 
verifyDescriptor(const AvbHashtreeDescriptor * desc)300 Result<std::unique_ptr<AvbHashtreeDescriptor>> verifyDescriptor(
301     const AvbHashtreeDescriptor* desc) {
302   auto verifiedDesc = std::make_unique<AvbHashtreeDescriptor>();
303 
304   if (!avb_hashtree_descriptor_validate_and_byteswap(desc,
305                                                      verifiedDesc.get())) {
306     return Errorf("Couldn't validate AvbDescriptor.");
307   }
308 
309   return verifiedDesc;
310 }
311 
312 }  // namespace
313 
VerifyApexVerity() const314 Result<ApexVerityData> ApexFile::VerifyApexVerity() const {
315   ApexVerityData verityData;
316 
317   unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
318   if (fd.get() == -1) {
319     return ErrnoError() << "Failed to open " << GetPath();
320   }
321 
322   Result<std::unique_ptr<AvbFooter>> footer = getAvbFooter(*this, fd);
323   if (!footer.ok()) {
324     return footer.error();
325   }
326 
327   Result<std::unique_ptr<uint8_t[]>> vbmeta_data =
328       verifyVbMeta(*this, fd, **footer);
329   if (!vbmeta_data.ok()) {
330     return vbmeta_data.error();
331   }
332 
333   Result<const AvbHashtreeDescriptor*> descriptor =
334       findDescriptor(vbmeta_data->get(), (*footer)->vbmeta_size);
335   if (!descriptor.ok()) {
336     return descriptor.error();
337   }
338 
339   Result<std::unique_ptr<AvbHashtreeDescriptor>> verifiedDescriptor =
340       verifyDescriptor(*descriptor);
341   if (!verifiedDescriptor.ok()) {
342     return verifiedDescriptor.error();
343   }
344   verityData.desc = std::move(*verifiedDescriptor);
345 
346   // This area is now safe to access, because we just verified it
347   const uint8_t* trailingData =
348       (const uint8_t*)*descriptor + sizeof(AvbHashtreeDescriptor);
349   verityData.hash_algorithm =
350       reinterpret_cast<const char*>((*descriptor)->hash_algorithm);
351   verityData.salt = getSalt(*verityData.desc, trailingData);
352   verityData.root_digest = getDigest(*verityData.desc, trailingData);
353 
354   return verityData;
355 }
356 
VerifyManifestMatches(const std::string & mount_path) const357 Result<void> ApexFile::VerifyManifestMatches(
358     const std::string& mount_path) const {
359   Result<ApexManifest> verifiedManifest =
360       ReadManifest(mount_path + "/" + kManifestFilenamePb);
361   if (!verifiedManifest.ok()) {
362     return verifiedManifest.error();
363   }
364 
365   if (!MessageDifferencer::Equals(manifest_, *verifiedManifest)) {
366     return Errorf(
367         "Manifest inside filesystem does not match manifest outside it");
368   }
369 
370   return {};
371 }
372 
FindApexes(const std::vector<std::string> & paths)373 Result<std::vector<std::string>> FindApexes(
374     const std::vector<std::string>& paths) {
375   std::vector<std::string> result;
376   for (const auto& path : paths) {
377     auto exist = PathExists(path);
378     if (!exist.ok()) {
379       return exist.error();
380     }
381     if (!*exist) continue;
382 
383     const auto& apexes = FindApexFilesByName(path);
384     if (!apexes.ok()) {
385       return apexes;
386     }
387 
388     result.insert(result.end(), apexes->begin(), apexes->end());
389   }
390   return result;
391 }
392 
FindApexFilesByName(const std::string & path)393 Result<std::vector<std::string>> FindApexFilesByName(const std::string& path) {
394   auto filter_fn = [](const std::filesystem::directory_entry& entry) {
395     std::error_code ec;
396     if (entry.is_regular_file(ec) &&
397         EndsWith(entry.path().filename().string(), kApexPackageSuffix)) {
398       return true;  // APEX file, take.
399     }
400     return false;
401   };
402   return ReadDir(path, filter_fn);
403 }
404 
isPathForBuiltinApexes(const std::string & path)405 bool isPathForBuiltinApexes(const std::string& path) {
406   for (const auto& dir : kApexPackageBuiltinDirs) {
407     if (StartsWith(path, dir)) {
408       return true;
409     }
410   }
411   return false;
412 }
413 
414 }  // namespace apex
415 }  // namespace android
416