• 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_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 int kVbMetaMaxSize = 64 * 1024;
124 
bytes_to_hex(const uint8_t * bytes,size_t bytes_len)125 std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
126   std::ostringstream s;
127 
128   s << std::hex << std::setfill('0');
129   for (size_t i = 0; i < bytes_len; i++) {
130     s << std::setw(2) << static_cast<int>(bytes[i]);
131   }
132   return s.str();
133 }
134 
getSalt(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)135 std::string getSalt(const AvbHashtreeDescriptor& desc,
136                     const uint8_t* trailingData) {
137   const uint8_t* desc_salt = trailingData + desc.partition_name_len;
138 
139   return bytes_to_hex(desc_salt, desc.salt_len);
140 }
141 
getDigest(const AvbHashtreeDescriptor & desc,const uint8_t * trailingData)142 std::string getDigest(const AvbHashtreeDescriptor& desc,
143                       const uint8_t* trailingData) {
144   const uint8_t* desc_digest =
145       trailingData + desc.partition_name_len + desc.salt_len;
146 
147   return bytes_to_hex(desc_digest, desc.root_digest_len);
148 }
149 
getAvbFooter(const ApexFile & apex,const unique_fd & fd)150 Result<std::unique_ptr<AvbFooter>> getAvbFooter(const ApexFile& apex,
151                                                 const unique_fd& fd) {
152   std::array<uint8_t, AVB_FOOTER_SIZE> footer_data;
153   auto footer = std::make_unique<AvbFooter>();
154 
155   // The AVB footer is located in the last part of the image
156   off_t offset = apex.GetImageSize() + apex.GetImageOffset() - AVB_FOOTER_SIZE;
157   int ret = lseek(fd, offset, SEEK_SET);
158   if (ret == -1) {
159     return ErrnoError() << "Couldn't seek to AVB footer";
160   }
161 
162   ret = read(fd, footer_data.data(), AVB_FOOTER_SIZE);
163   if (ret != AVB_FOOTER_SIZE) {
164     return ErrnoError() << "Couldn't read AVB footer";
165   }
166 
167   if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_data.data(),
168                                         footer.get())) {
169     return Error() << "AVB footer verification failed.";
170   }
171 
172   LOG(VERBOSE) << "AVB footer verification successful.";
173   return footer;
174 }
175 
CompareKeys(const uint8_t * key,size_t length,const std::string & public_key_content)176 bool CompareKeys(const uint8_t* key, size_t length,
177                  const std::string& public_key_content) {
178   return public_key_content.length() == length &&
179          memcmp(&public_key_content[0], key, length) == 0;
180 }
181 
verifyVbMetaSignature(const ApexFile & apex,const uint8_t * data,size_t length)182 Result<void> verifyVbMetaSignature(const ApexFile& apex, const uint8_t* data,
183                                    size_t length) {
184   const uint8_t* pk;
185   size_t pk_len;
186   AvbVBMetaVerifyResult res;
187 
188   res = avb_vbmeta_image_verify(data, length, &pk, &pk_len);
189   switch (res) {
190     case AVB_VBMETA_VERIFY_RESULT_OK:
191       break;
192     case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
193     case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
194     case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
195       return Error() << "Error verifying " << apex.GetPath() << ": "
196                      << avb_vbmeta_verify_result_to_string(res);
197     case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
198       return Error() << "Error verifying " << apex.GetPath() << ": "
199                      << "invalid vbmeta header";
200     case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
201       return Error() << "Error verifying " << apex.GetPath() << ": "
202                      << "unsupported version";
203     default:
204       return Errorf("Unknown vmbeta_image_verify return value");
205   }
206 
207   Result<const std::string> public_key = getApexKey(apex.GetManifest().name());
208   if (public_key.ok()) {
209     // TODO(b/115718846)
210     // We need to decide whether we need rollback protection, and whether
211     // we can use the rollback protection provided by libavb.
212     if (!CompareKeys(pk, pk_len, *public_key)) {
213       return Error() << "Error verifying " << apex.GetPath() << ": "
214                      << "public key doesn't match the pre-installed one";
215     }
216   } else {
217     return public_key.error();
218   }
219   LOG(VERBOSE) << apex.GetPath() << ": public key matches.";
220   return {};
221 }
222 
verifyVbMeta(const ApexFile & apex,const unique_fd & fd,const AvbFooter & footer)223 Result<std::unique_ptr<uint8_t[]>> verifyVbMeta(const ApexFile& apex,
224                                                 const unique_fd& fd,
225                                                 const AvbFooter& footer) {
226   if (footer.vbmeta_size > kVbMetaMaxSize) {
227     return Errorf("VbMeta size in footer exceeds kVbMetaMaxSize.");
228   }
229 
230   off_t offset = apex.GetImageOffset() + footer.vbmeta_offset;
231   std::unique_ptr<uint8_t[]> vbmeta_buf(new uint8_t[footer.vbmeta_size]);
232 
233   if (!ReadFullyAtOffset(fd, vbmeta_buf.get(), footer.vbmeta_size, offset)) {
234     return ErrnoError() << "Couldn't read AVB meta-data";
235   }
236 
237   Result<void> st =
238       verifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size);
239   if (!st.ok()) {
240     return st.error();
241   }
242 
243   return vbmeta_buf;
244 }
245 
findDescriptor(uint8_t * vbmeta_data,size_t vbmeta_size)246 Result<const AvbHashtreeDescriptor*> findDescriptor(uint8_t* vbmeta_data,
247                                                     size_t vbmeta_size) {
248   const AvbDescriptor** descriptors;
249   size_t num_descriptors;
250 
251   descriptors =
252       avb_descriptor_get_all(vbmeta_data, vbmeta_size, &num_descriptors);
253 
254   // avb_descriptor_get_all() returns an internally allocated array
255   // of pointers and it needs to be avb_free()ed after using it.
256   auto guard = android::base::ScopeGuard(std::bind(avb_free, descriptors));
257 
258   for (size_t i = 0; i < num_descriptors; i++) {
259     AvbDescriptor desc;
260     if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
261       return Errorf("Couldn't validate AvbDescriptor.");
262     }
263 
264     if (desc.tag != AVB_DESCRIPTOR_TAG_HASHTREE) {
265       // Ignore other descriptors
266       continue;
267     }
268 
269     // Check that hashtree descriptor actually fits into memory.
270     const uint8_t* vbmeta_end = vbmeta_data + vbmeta_size;
271     if ((uint8_t*)descriptors[i] + sizeof(AvbHashtreeDescriptor) > vbmeta_end) {
272       return Errorf("Invalid length for AvbHashtreeDescriptor");
273     }
274     return (const AvbHashtreeDescriptor*)descriptors[i];
275   }
276 
277   return Errorf("Couldn't find any AVB hashtree descriptors.");
278 }
279 
verifyDescriptor(const AvbHashtreeDescriptor * desc)280 Result<std::unique_ptr<AvbHashtreeDescriptor>> verifyDescriptor(
281     const AvbHashtreeDescriptor* desc) {
282   auto verifiedDesc = std::make_unique<AvbHashtreeDescriptor>();
283 
284   if (!avb_hashtree_descriptor_validate_and_byteswap(desc,
285                                                      verifiedDesc.get())) {
286     return Errorf("Couldn't validate AvbDescriptor.");
287   }
288 
289   return verifiedDesc;
290 }
291 
292 }  // namespace
293 
VerifyApexVerity() const294 Result<ApexVerityData> ApexFile::VerifyApexVerity() const {
295   ApexVerityData verityData;
296 
297   unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
298   if (fd.get() == -1) {
299     return ErrnoError() << "Failed to open " << GetPath();
300   }
301 
302   Result<std::unique_ptr<AvbFooter>> footer = getAvbFooter(*this, fd);
303   if (!footer.ok()) {
304     return footer.error();
305   }
306 
307   Result<std::unique_ptr<uint8_t[]>> vbmeta_data =
308       verifyVbMeta(*this, fd, **footer);
309   if (!vbmeta_data.ok()) {
310     return vbmeta_data.error();
311   }
312 
313   Result<const AvbHashtreeDescriptor*> descriptor =
314       findDescriptor(vbmeta_data->get(), (*footer)->vbmeta_size);
315   if (!descriptor.ok()) {
316     return descriptor.error();
317   }
318 
319   Result<std::unique_ptr<AvbHashtreeDescriptor>> verifiedDescriptor =
320       verifyDescriptor(*descriptor);
321   if (!verifiedDescriptor.ok()) {
322     return verifiedDescriptor.error();
323   }
324   verityData.desc = std::move(*verifiedDescriptor);
325 
326   // This area is now safe to access, because we just verified it
327   const uint8_t* trailingData =
328       (const uint8_t*)*descriptor + sizeof(AvbHashtreeDescriptor);
329   verityData.hash_algorithm =
330       reinterpret_cast<const char*>((*descriptor)->hash_algorithm);
331   verityData.salt = getSalt(*verityData.desc, trailingData);
332   verityData.root_digest = getDigest(*verityData.desc, trailingData);
333 
334   return verityData;
335 }
336 
VerifyManifestMatches(const std::string & mount_path) const337 Result<void> ApexFile::VerifyManifestMatches(
338     const std::string& mount_path) const {
339   Result<ApexManifest> verifiedManifest =
340       ReadManifest(mount_path + "/" + kManifestFilenamePb);
341   if (!verifiedManifest.ok()) {
342     return verifiedManifest.error();
343   }
344 
345   if (!MessageDifferencer::Equals(manifest_, *verifiedManifest)) {
346     return Errorf(
347         "Manifest inside filesystem does not match manifest outside it");
348   }
349 
350   return {};
351 }
352 
FindApexes(const std::vector<std::string> & paths)353 Result<std::vector<std::string>> FindApexes(
354     const std::vector<std::string>& paths) {
355   std::vector<std::string> result;
356   for (const auto& path : paths) {
357     auto exist = PathExists(path);
358     if (!exist.ok()) {
359       return exist.error();
360     }
361     if (!*exist) continue;
362 
363     const auto& apexes = FindApexFilesByName(path);
364     if (!apexes.ok()) {
365       return apexes;
366     }
367 
368     result.insert(result.end(), apexes->begin(), apexes->end());
369   }
370   return result;
371 }
372 
FindApexFilesByName(const std::string & path)373 Result<std::vector<std::string>> FindApexFilesByName(const std::string& path) {
374   auto filter_fn = [](const std::filesystem::directory_entry& entry) {
375     std::error_code ec;
376     if (entry.is_regular_file(ec) &&
377         EndsWith(entry.path().filename().string(), kApexPackageSuffix)) {
378       return true;  // APEX file, take.
379     }
380     return false;
381   };
382   return ReadDir(path, filter_fn);
383 }
384 
isPathForBuiltinApexes(const std::string & path)385 bool isPathForBuiltinApexes(const std::string& path) {
386   for (const auto& dir : kApexPackageBuiltinDirs) {
387     if (StartsWith(path, dir)) {
388       return true;
389     }
390   }
391   return false;
392 }
393 
394 }  // namespace apex
395 }  // namespace android
396