blob: 2ef9511ddbe5bc4ec899b8ab04e41e5367a44c0a [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
53 int fd = open(path.c_str(), O_RDONLY);
54 pid_t pid = fork();
55 if (pid == 0) {
56 dup2(fd, STDIN_FILENO);
57 close(fd);
58 int argc = arraysize(argv);
59 char* argv_child[argc + 1];
60 memcpy(argv_child, argv, argc * sizeof(char*));
61 argv_child[argc] = nullptr;
62 execvp(argv_child[0], const_cast<char**>(argv_child));
63 PLOG(ERROR) << "exec in ForkExecvp";
64 _exit(EXIT_FAILURE);
65 } else {
66 close(fd);
67 }
68 if (pid == -1) {
69 return ErrnoError() << "Failed to fork.";
70 }
71 int status;
72 if (waitpid(pid, &status, 0) == -1) {
73 return ErrnoError() << "waitpid() failed.";
74 }
75 if (!WIFEXITED(status)) {
76 return Error() << kFsVerityInitPath << ": abnormal process exit";
77 }
78 if (WEXITSTATUS(status)) {
79 if (status != 0) {
80 return Error() << kFsVerityInitPath << " exited with " << status;
81 }
82 }
83
84 return {};
85}
86
87Result<KeymasterSigningKey> loadAndVerifyExistingKey() {
88 if (access(kSigningKeyBlob.c_str(), F_OK) < 0) {
89 return ErrnoError() << "Key blob not found: " << kSigningKeyBlob;
90 }
91 return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
92}
93
94Result<void> verifyAndLoadExistingCert(const KeymasterSigningKey& key) {
95 if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
96 return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
97 }
98 auto trustedPublicKey = key.getPublicKey();
99 if (!trustedPublicKey.ok()) {
100 return Error() << "Failed to retrieve signing public key.";
101 }
102
103 auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
104 if (!publicKeyFromExistingCert.ok()) {
105 return publicKeyFromExistingCert.error();
106 }
107 if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
108 return Error() << "Public key of existing certificate at " << kSigningKeyCert
109 << " does not match signing public key.";
110 }
111
112 auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
113 if (!cert_add_result.ok()) {
114 return cert_add_result.error();
115 }
116
117 // At this point, we know the cert matches
118 return {};
119}
120
121Result<KeymasterSigningKey> createAndPersistKey(const std::string& path) {
122 auto key = KeymasterSigningKey::createNewKey();
123
124 if (!key.ok()) {
125 return key.error();
126 }
127
128 auto result = key->saveKeyblob(path);
129 if (!result.ok()) {
130 return result.error();
131 }
132
133 return key;
134}
135
136bool compileArtifacts(bool force) {
137 const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
138
139 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
140 0;
141}
142
143bool validateArtifacts() {
144 const char* const argv[] = {kOdrefreshPath, "--check"};
145
146 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
147 0;
148}
149
150int main(int /* argc */, char** /* argv */) {
151 auto removeArtifacts = []() {
152 std::error_code ec;
153 auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
154 if (ec) {
155 // TODO can't remove artifacts, signal Zygote shouldn't use them
156 LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
157 } else {
158 LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
159 }
160 };
161 // Make sure we delete the artifacts in all early (error) exit paths
162 auto scope_guard = android::base::make_scope_guard(removeArtifacts);
163
164 auto key = loadAndVerifyExistingKey();
165 if (!key.ok()) {
166 LOG(WARNING) << key.error().message();
167
168 key = createAndPersistKey(kSigningKeyBlob);
169 if (!key.ok()) {
170 LOG(ERROR) << "Failed to create or persist new key: " << key.error().message();
171 return -1;
172 }
173 } else {
174 LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
175 }
176
177 auto existing_cert = verifyAndLoadExistingCert(key.value());
178 if (!existing_cert.ok()) {
179 LOG(WARNING) << existing_cert.error().message();
180
181 // Try to create a new cert
182 auto new_cert = key->createX509Cert(kSigningKeyCert);
183 if (!new_cert.ok()) {
184 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
185 // TODO apparently the key become invalid - delete the blob / cert
186 return -1;
187 }
188 auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
189 if (!cert_add_result.ok()) {
190 LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
191 << cert_add_result.error().message();
192 return -1;
193 }
194 } else {
195 LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
196 }
197
198 auto verityStatus = verifyAllFilesInVerity(kArtArtifactsDir);
199 if (!verityStatus.ok()) {
200 LOG(WARNING) << verityStatus.error().message() << ", removing " << kArtArtifactsDir;
201 removeArtifacts();
202 }
203
204 bool artifactsValid = validateArtifacts();
205
206 if (!artifactsValid || kForceCompilation) {
207 removeArtifacts();
208
209 LOG(INFO) << "Starting compilation... ";
210 bool ret = compileArtifacts(kForceCompilation);
211 LOG(INFO) << "Compilation done, returned " << ret;
212
213 verityStatus = addFilesToVerityRecursive(kArtArtifactsDir, key.value());
214
215 if (!verityStatus.ok()) {
216 LOG(ERROR) << "Failed to add " << verityStatus.error().message();
217 return -1;
218 }
219 }
220
221 // TODO we want to make sure Zygote only picks up the artifacts if we deemed
222 // everything was ok here. We could use a sysprop, or some other mechanism?
223 LOG(INFO) << "On-device signing done.";
224
225 scope_guard.Disable();
226 return 0;
227}