blob: 1c63ecbce47b2c57a7b7d9357fe0c7b64c3653cb [file] [log] [blame]
Martijn Coenen95194842020-09-24 16:56:46 +02001/*
2 * Copyright (C) 2020 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 <fcntl.h>
18#include <filesystem>
Martijn Coenen5588e492021-02-25 14:33:44 +010019#include <fstream>
Martijn Coenen95194842020-09-24 16:56:46 +020020#include <iomanip>
21#include <iostream>
Martijn Coenen5588e492021-02-25 14:33:44 +010022#include <iterator>
Martijn Coenen95194842020-09-24 16:56:46 +020023#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26#include <unistd.h>
27
28#include <android-base/file.h>
29#include <android-base/logging.h>
30#include <android-base/scopeguard.h>
31#include <logwrap/logwrap.h>
32
33#include "CertUtils.h"
34#include "KeymasterSigningKey.h"
35#include "VerityUtils.h"
36
Martijn Coenen5588e492021-02-25 14:33:44 +010037#include "odsign_info.pb.h"
38
Martijn Coenen95194842020-09-24 16:56:46 +020039using android::base::ErrnoError;
40using android::base::Error;
41using android::base::Result;
42
Martijn Coenen5588e492021-02-25 14:33:44 +010043using OdsignInfo = ::odsign::proto::OdsignInfo;
44
Martijn Coenen95194842020-09-24 16:56:46 +020045const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
46const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
Martijn Coenen5588e492021-02-25 14:33:44 +010047const std::string kOdsignInfo = "/data/misc/odsign/odsign.info";
48const std::string kOdsignInfoSignature = "/data/misc/odsign/odsign.info.signature";
Martijn Coenen95194842020-09-24 16:56:46 +020049
Martijn Coenenb6afe132021-02-19 09:32:48 +010050const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/dalvik-cache";
Martijn Coenen95194842020-09-24 16:56:46 +020051
52static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
53
54static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
Martijn Coenen5588e492021-02-25 14:33:44 +010055static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
Martijn Coenen95194842020-09-24 16:56:46 +020056
57static const bool kForceCompilation = false;
58
59Result<void> addCertToFsVerityKeyring(const std::string& path) {
60 const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
61
Martijn Coenen0186b0f2021-02-24 09:31:59 +010062 // NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
Martijn Coenen95194842020-09-24 16:56:46 +020063 int fd = open(path.c_str(), O_RDONLY);
64 pid_t pid = fork();
65 if (pid == 0) {
66 dup2(fd, STDIN_FILENO);
67 close(fd);
68 int argc = arraysize(argv);
69 char* argv_child[argc + 1];
70 memcpy(argv_child, argv, argc * sizeof(char*));
71 argv_child[argc] = nullptr;
72 execvp(argv_child[0], const_cast<char**>(argv_child));
73 PLOG(ERROR) << "exec in ForkExecvp";
74 _exit(EXIT_FAILURE);
75 } else {
76 close(fd);
77 }
78 if (pid == -1) {
79 return ErrnoError() << "Failed to fork.";
80 }
81 int status;
82 if (waitpid(pid, &status, 0) == -1) {
83 return ErrnoError() << "waitpid() failed.";
84 }
85 if (!WIFEXITED(status)) {
86 return Error() << kFsVerityInitPath << ": abnormal process exit";
87 }
88 if (WEXITSTATUS(status)) {
89 if (status != 0) {
90 return Error() << kFsVerityInitPath << " exited with " << status;
91 }
92 }
93
94 return {};
95}
96
97Result<KeymasterSigningKey> loadAndVerifyExistingKey() {
98 if (access(kSigningKeyBlob.c_str(), F_OK) < 0) {
99 return ErrnoError() << "Key blob not found: " << kSigningKeyBlob;
100 }
101 return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
102}
103
Martijn Coenen799ce6e2021-02-23 10:14:41 +0100104Result<void> verifyExistingCert(const KeymasterSigningKey& key) {
Martijn Coenen95194842020-09-24 16:56:46 +0200105 if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
106 return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
107 }
108 auto trustedPublicKey = key.getPublicKey();
109 if (!trustedPublicKey.ok()) {
110 return Error() << "Failed to retrieve signing public key.";
111 }
112
113 auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
114 if (!publicKeyFromExistingCert.ok()) {
115 return publicKeyFromExistingCert.error();
116 }
117 if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
118 return Error() << "Public key of existing certificate at " << kSigningKeyCert
119 << " does not match signing public key.";
120 }
121
Martijn Coenen95194842020-09-24 16:56:46 +0200122 // At this point, we know the cert matches
123 return {};
124}
125
126Result<KeymasterSigningKey> createAndPersistKey(const std::string& path) {
127 auto key = KeymasterSigningKey::createNewKey();
128
129 if (!key.ok()) {
130 return key.error();
131 }
132
133 auto result = key->saveKeyblob(path);
134 if (!result.ok()) {
135 return result.error();
136 }
137
138 return key;
139}
140
141bool compileArtifacts(bool force) {
142 const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
143
144 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
145 0;
146}
147
148bool validateArtifacts() {
149 const char* const argv[] = {kOdrefreshPath, "--check"};
150
151 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
152 0;
153}
154
Martijn Coenen5588e492021-02-25 14:33:44 +0100155static std::string toHex(const std::vector<uint8_t>& digest) {
156 std::stringstream ss;
157 for (auto it = digest.begin(); it != digest.end(); ++it) {
158 ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
159 }
160 return ss.str();
161}
162
163Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
164 std::error_code ec;
165 std::map<std::string, std::string> digests;
166
167 auto it = std::filesystem::recursive_directory_iterator(path, ec);
168 auto end = std::filesystem::recursive_directory_iterator();
169
170 while (!ec && it != end) {
171 if (it->is_regular_file()) {
172 auto digest = createDigest(it->path());
173 if (!digest.ok()) {
174 return Error() << "Failed to compute digest for " << it->path();
175 }
176 digests[it->path()] = toHex(*digest);
177 }
178 ++it;
179 }
180 if (ec) {
181 return Error() << "Failed to iterate " << path << ": " << ec;
182 }
183
184 return digests;
185}
186
187Result<void> verifyDigests(const std::map<std::string, std::string>& digests,
188 const std::map<std::string, std::string>& trusted_digests) {
189 for (const auto& path_digest : digests) {
190 auto path = path_digest.first;
191 auto digest = path_digest.second;
192 if ((trusted_digests.count(path) == 0)) {
193 return Error() << "Couldn't find digest for " << path;
194 }
195 if (trusted_digests.at(path) != digest) {
196 return Error() << "Digest mismatch for " << path;
197 }
198 }
199
200 // All digests matched!
201 if (digests.size() > 0) {
202 LOG(INFO) << "All root hashes match.";
203 }
204 return {};
205}
206
207Result<void> verifyIntegrityFsVerity(const std::map<std::string, std::string>& trusted_digests) {
208 // Just verify that the files are in verity, and get their digests
209 auto result = verifyAllFilesInVerity(kArtArtifactsDir);
210 if (!result.ok()) {
211 return result.error();
212 }
213
214 return verifyDigests(*result, trusted_digests);
215}
216
217Result<void> verifyIntegrityNoFsVerity(const std::map<std::string, std::string>& trusted_digests) {
218 // On these devices, just compute the digests, and verify they match the ones we trust
219 auto result = computeDigests(kArtArtifactsDir);
220 if (!result.ok()) {
221 return result.error();
222 }
223
224 return verifyDigests(*result, trusted_digests);
225}
226
227Result<OdsignInfo> getOdsignInfo(const KeymasterSigningKey& /* key */) {
228 std::string persistedSignature;
229 OdsignInfo odsignInfo;
230
231 if (!android::base::ReadFileToString(kOdsignInfoSignature, &persistedSignature)) {
232 return ErrnoError() << "Failed to read " << kOdsignInfoSignature;
233 }
234
235 std::fstream odsign_info(kOdsignInfo, std::ios::in | std::ios::binary);
236 if (!odsign_info) {
237 return Error() << "Failed to open " << kOdsignInfo;
238 }
239 // Verify the hash
240 std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
241 std::istreambuf_iterator<char>());
242
243 // TODO: this is way too slow - replace with a local RSA_verify implementation.
244 /*
245 auto verifiedSignature = key.sign(odsign_info_str);
246 if (!verifiedSignature.ok()) {
247 return Error() << "Failed to sign " << kOdsignInfo;
248 }
249
250 if (*verifiedSignature != persistedSignature) {
251 return Error() << kOdsignInfoSignature << " does not match.";
252 } else {
253 LOG(INFO) << kOdsignInfoSignature << " matches.";
254 }
255 */
256
257 if (!odsignInfo.ParseFromIstream(&odsign_info)) {
258 return Error() << "Failed to parse " << kOdsignInfo;
259 }
260
261 LOG(INFO) << "Loaded " << kOdsignInfo;
262 return odsignInfo;
263}
264
265Result<void> persistDigests(const std::map<std::string, std::string>& digests,
266 const KeymasterSigningKey& key) {
267 OdsignInfo signInfo;
268 google::protobuf::Map<std::string, std::string> proto_hashes(digests.begin(), digests.end());
269 auto map = signInfo.mutable_file_hashes();
270 *map = proto_hashes;
271
272 std::fstream odsign_info(kOdsignInfo, std::ios::out | std::ios::trunc | std::ios::binary);
273 if (!signInfo.SerializeToOstream(&odsign_info)) {
274 return Error() << "Failed to persist root hashes in " << kOdsignInfo;
275 }
276
277 odsign_info.seekg(0);
278 std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
279 std::istreambuf_iterator<char>());
280 auto signResult = key.sign(odsign_info_str);
281 if (!signResult.ok()) {
282 return Error() << "Failed to sign " << kOdsignInfo;
283 }
284 // Sign the files with our key itself, and write that to storage
285 android::base::WriteStringToFile(*signResult, kOdsignInfoSignature);
286 return {};
287}
288
Martijn Coenen95194842020-09-24 16:56:46 +0200289int main(int /* argc */, char** /* argv */) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100290 auto removeArtifacts = []() -> std::uintmax_t {
Martijn Coenen95194842020-09-24 16:56:46 +0200291 std::error_code ec;
292 auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
293 if (ec) {
294 // TODO can't remove artifacts, signal Zygote shouldn't use them
295 LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
Martijn Coenen5588e492021-02-25 14:33:44 +0100296 return 0;
Martijn Coenen95194842020-09-24 16:56:46 +0200297 } else {
Martijn Coenen5588e492021-02-25 14:33:44 +0100298 if (num_removed > 0) {
299 LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
300 }
301 return num_removed;
Martijn Coenen95194842020-09-24 16:56:46 +0200302 }
303 };
304 // Make sure we delete the artifacts in all early (error) exit paths
305 auto scope_guard = android::base::make_scope_guard(removeArtifacts);
306
307 auto key = loadAndVerifyExistingKey();
308 if (!key.ok()) {
309 LOG(WARNING) << key.error().message();
310
311 key = createAndPersistKey(kSigningKeyBlob);
312 if (!key.ok()) {
313 LOG(ERROR) << "Failed to create or persist new key: " << key.error().message();
314 return -1;
315 }
316 } else {
317 LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
318 }
319
Martijn Coenen5588e492021-02-25 14:33:44 +0100320 bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
321 if (!supportsFsVerity) {
322 LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
323 }
Martijn Coenen95194842020-09-24 16:56:46 +0200324
Martijn Coenen5588e492021-02-25 14:33:44 +0100325 if (supportsFsVerity) {
326 auto existing_cert = verifyExistingCert(key.value());
327 if (!existing_cert.ok()) {
328 LOG(WARNING) << existing_cert.error().message();
329
330 // Try to create a new cert
331 auto new_cert = key->createX509Cert(kSigningKeyCert);
332 if (!new_cert.ok()) {
333 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
334 // TODO apparently the key become invalid - delete the blob / cert
335 return -1;
336 }
337 } else {
338 LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
339 }
340 auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
341 if (!cert_add_result.ok()) {
342 LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
343 << cert_add_result.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200344 return -1;
345 }
Martijn Coenen5588e492021-02-25 14:33:44 +0100346 }
347
348 auto signInfo = getOdsignInfo(key.value());
349 if (!signInfo.ok()) {
350 int num_removed = removeArtifacts();
351 // Only a warning if there were artifacts to begin with, which suggests tampering or
352 // corruption
353 if (num_removed > 0) {
354 LOG(WARNING) << signInfo.error().message();
355 }
Martijn Coenen95194842020-09-24 16:56:46 +0200356 } else {
Martijn Coenen5588e492021-02-25 14:33:44 +0100357 std::map<std::string, std::string> trusted_digests(signInfo->file_hashes().begin(),
358 signInfo->file_hashes().end());
359 Result<void> integrityStatus;
360
361 if (supportsFsVerity) {
362 integrityStatus = verifyIntegrityFsVerity(trusted_digests);
363 } else {
364 integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
365 }
366 if (!integrityStatus.ok()) {
367 LOG(WARNING) << integrityStatus.error().message() << ", removing " << kArtArtifactsDir;
368 removeArtifacts();
369 }
Martijn Coenen799ce6e2021-02-23 10:14:41 +0100370 }
Martijn Coenen95194842020-09-24 16:56:46 +0200371
Martijn Coenen5588e492021-02-25 14:33:44 +0100372 // Ask ART whether it considers the artifacts valid
373 LOG(INFO) << "Asking odrefresh to verify artifacts (if present)...";
Martijn Coenen95194842020-09-24 16:56:46 +0200374 bool artifactsValid = validateArtifacts();
Martijn Coenen5588e492021-02-25 14:33:44 +0100375 LOG(INFO) << "odrefresh said they are " << (artifactsValid ? "VALID" : "INVALID");
Martijn Coenen95194842020-09-24 16:56:46 +0200376
377 if (!artifactsValid || kForceCompilation) {
Martijn Coenen95194842020-09-24 16:56:46 +0200378 LOG(INFO) << "Starting compilation... ";
379 bool ret = compileArtifacts(kForceCompilation);
380 LOG(INFO) << "Compilation done, returned " << ret;
381
Martijn Coenen5588e492021-02-25 14:33:44 +0100382 Result<std::map<std::string, std::string>> digests;
383 if (supportsFsVerity) {
384 digests = addFilesToVerityRecursive(kArtArtifactsDir, key.value());
385 } else {
386 // If we can't use verity, just compute the root hashes and store
387 // those, so we can reverify them at the next boot.
388 digests = computeDigests(kArtArtifactsDir);
389 }
390 if (!digests.ok()) {
391 LOG(ERROR) << digests.error().message();
392 return -1;
393 }
Martijn Coenen95194842020-09-24 16:56:46 +0200394
Martijn Coenen5588e492021-02-25 14:33:44 +0100395 auto persistStatus = persistDigests(*digests, key.value());
396 if (!persistStatus.ok()) {
397 LOG(ERROR) << persistStatus.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200398 return -1;
399 }
400 }
401
402 // TODO we want to make sure Zygote only picks up the artifacts if we deemed
403 // everything was ok here. We could use a sysprop, or some other mechanism?
404 LOG(INFO) << "On-device signing done.";
405
406 scope_guard.Disable();
407 return 0;
408}