blob: 3baba68972465d68570f9e5c3c5a2160c1bf780a [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>
19#include <iomanip>
20#include <iostream>
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <sys/wait.h>
24#include <unistd.h>
25
26#include <android-base/file.h>
27#include <android-base/logging.h>
28#include <android-base/scopeguard.h>
29#include <logwrap/logwrap.h>
30
31#include "CertUtils.h"
32#include "KeymasterSigningKey.h"
33#include "VerityUtils.h"
34
35using android::base::ErrnoError;
36using android::base::Error;
37using android::base::Result;
38
39const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
40const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
41
Martijn Coenenb6afe132021-02-19 09:32:48 +010042const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/dalvik-cache";
Martijn Coenen95194842020-09-24 16:56:46 +020043
44static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
45
46static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
47
48static const bool kForceCompilation = false;
49
50Result<void> addCertToFsVerityKeyring(const std::string& path) {
51 const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
52
Martijn Coenen0186b0f2021-02-24 09:31:59 +010053 // NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
Martijn Coenen95194842020-09-24 16:56:46 +020054 int fd = open(path.c_str(), O_RDONLY);
55 pid_t pid = fork();
56 if (pid == 0) {
57 dup2(fd, STDIN_FILENO);
58 close(fd);
59 int argc = arraysize(argv);
60 char* argv_child[argc + 1];
61 memcpy(argv_child, argv, argc * sizeof(char*));
62 argv_child[argc] = nullptr;
63 execvp(argv_child[0], const_cast<char**>(argv_child));
64 PLOG(ERROR) << "exec in ForkExecvp";
65 _exit(EXIT_FAILURE);
66 } else {
67 close(fd);
68 }
69 if (pid == -1) {
70 return ErrnoError() << "Failed to fork.";
71 }
72 int status;
73 if (waitpid(pid, &status, 0) == -1) {
74 return ErrnoError() << "waitpid() failed.";
75 }
76 if (!WIFEXITED(status)) {
77 return Error() << kFsVerityInitPath << ": abnormal process exit";
78 }
79 if (WEXITSTATUS(status)) {
80 if (status != 0) {
81 return Error() << kFsVerityInitPath << " exited with " << status;
82 }
83 }
84
85 return {};
86}
87
88Result<KeymasterSigningKey> loadAndVerifyExistingKey() {
89 if (access(kSigningKeyBlob.c_str(), F_OK) < 0) {
90 return ErrnoError() << "Key blob not found: " << kSigningKeyBlob;
91 }
92 return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
93}
94
Martijn Coenen799ce6e2021-02-23 10:14:41 +010095Result<void> verifyExistingCert(const KeymasterSigningKey& key) {
Martijn Coenen95194842020-09-24 16:56:46 +020096 if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
97 return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
98 }
99 auto trustedPublicKey = key.getPublicKey();
100 if (!trustedPublicKey.ok()) {
101 return Error() << "Failed to retrieve signing public key.";
102 }
103
104 auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
105 if (!publicKeyFromExistingCert.ok()) {
106 return publicKeyFromExistingCert.error();
107 }
108 if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
109 return Error() << "Public key of existing certificate at " << kSigningKeyCert
110 << " does not match signing public key.";
111 }
112
Martijn Coenen95194842020-09-24 16:56:46 +0200113 // At this point, we know the cert matches
114 return {};
115}
116
117Result<KeymasterSigningKey> createAndPersistKey(const std::string& path) {
118 auto key = KeymasterSigningKey::createNewKey();
119
120 if (!key.ok()) {
121 return key.error();
122 }
123
124 auto result = key->saveKeyblob(path);
125 if (!result.ok()) {
126 return result.error();
127 }
128
129 return key;
130}
131
132bool compileArtifacts(bool force) {
133 const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
134
135 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
136 0;
137}
138
139bool validateArtifacts() {
140 const char* const argv[] = {kOdrefreshPath, "--check"};
141
142 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
143 0;
144}
145
146int main(int /* argc */, char** /* argv */) {
147 auto removeArtifacts = []() {
148 std::error_code ec;
149 auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
150 if (ec) {
151 // TODO can't remove artifacts, signal Zygote shouldn't use them
152 LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
153 } else {
154 LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
155 }
156 };
157 // Make sure we delete the artifacts in all early (error) exit paths
158 auto scope_guard = android::base::make_scope_guard(removeArtifacts);
159
160 auto key = loadAndVerifyExistingKey();
161 if (!key.ok()) {
162 LOG(WARNING) << key.error().message();
163
164 key = createAndPersistKey(kSigningKeyBlob);
165 if (!key.ok()) {
166 LOG(ERROR) << "Failed to create or persist new key: " << key.error().message();
167 return -1;
168 }
169 } else {
170 LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
171 }
172
Martijn Coenen799ce6e2021-02-23 10:14:41 +0100173 auto existing_cert = verifyExistingCert(key.value());
Martijn Coenen95194842020-09-24 16:56:46 +0200174 if (!existing_cert.ok()) {
175 LOG(WARNING) << existing_cert.error().message();
176
177 // Try to create a new cert
178 auto new_cert = key->createX509Cert(kSigningKeyCert);
179 if (!new_cert.ok()) {
180 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
181 // TODO apparently the key become invalid - delete the blob / cert
182 return -1;
183 }
Martijn Coenen95194842020-09-24 16:56:46 +0200184 } else {
185 LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
186 }
Martijn Coenen799ce6e2021-02-23 10:14:41 +0100187 auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
188 if (!cert_add_result.ok()) {
189 LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
190 << cert_add_result.error().message();
191 return -1;
192 }
Martijn Coenen95194842020-09-24 16:56:46 +0200193
194 auto verityStatus = verifyAllFilesInVerity(kArtArtifactsDir);
195 if (!verityStatus.ok()) {
196 LOG(WARNING) << verityStatus.error().message() << ", removing " << kArtArtifactsDir;
197 removeArtifacts();
198 }
199
200 bool artifactsValid = validateArtifacts();
201
202 if (!artifactsValid || kForceCompilation) {
203 removeArtifacts();
204
205 LOG(INFO) << "Starting compilation... ";
206 bool ret = compileArtifacts(kForceCompilation);
207 LOG(INFO) << "Compilation done, returned " << ret;
208
209 verityStatus = addFilesToVerityRecursive(kArtArtifactsDir, key.value());
210
211 if (!verityStatus.ok()) {
212 LOG(ERROR) << "Failed to add " << verityStatus.error().message();
213 return -1;
214 }
215 }
216
217 // TODO we want to make sure Zygote only picks up the artifacts if we deemed
218 // everything was ok here. We could use a sysprop, or some other mechanism?
219 LOG(INFO) << "On-device signing done.";
220
221 scope_guard.Disable();
222 return 0;
223}