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