blob: 853f34912b6eca0158970a4f000e9e1094a81d0b [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>
Martijn Coenen95194842020-09-24 16:56:46 +020025#include <unistd.h>
26
27#include <android-base/file.h>
28#include <android-base/logging.h>
29#include <android-base/scopeguard.h>
30#include <logwrap/logwrap.h>
31
32#include "CertUtils.h"
33#include "KeymasterSigningKey.h"
Martijn Coenenba1c9dc2021-02-04 13:18:29 +010034#include "KeystoreKey.h"
Martijn Coenen95194842020-09-24 16:56:46 +020035#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
Martijn Coenen5588e492021-02-25 14:33:44 +010054static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
Martijn Coenen95194842020-09-24 16:56:46 +020055
56static const bool kForceCompilation = false;
Martijn Coenenba1c9dc2021-02-04 13:18:29 +010057static const bool kUseKeystore = false;
Martijn Coenen95194842020-09-24 16:56:46 +020058
Martijn Coenenba1c9dc2021-02-04 13:18:29 +010059Result<void> verifyExistingCert(const SigningKey& key) {
Martijn Coenen95194842020-09-24 16:56:46 +020060 if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
61 return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
62 }
63 auto trustedPublicKey = key.getPublicKey();
64 if (!trustedPublicKey.ok()) {
65 return Error() << "Failed to retrieve signing public key.";
66 }
67
68 auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
69 if (!publicKeyFromExistingCert.ok()) {
70 return publicKeyFromExistingCert.error();
71 }
72 if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
73 return Error() << "Public key of existing certificate at " << kSigningKeyCert
74 << " does not match signing public key.";
75 }
76
Martijn Coenen95194842020-09-24 16:56:46 +020077 // At this point, we know the cert matches
78 return {};
79}
80
Martijn Coenenba1c9dc2021-02-04 13:18:29 +010081Result<void> createX509Cert(const SigningKey& key, const std::string& outPath) {
82 auto publicKey = key.getPublicKey();
Martijn Coenen95194842020-09-24 16:56:46 +020083
Martijn Coenenba1c9dc2021-02-04 13:18:29 +010084 if (!publicKey.ok()) {
85 return publicKey.error();
Martijn Coenen95194842020-09-24 16:56:46 +020086 }
87
Martijn Coenenba1c9dc2021-02-04 13:18:29 +010088 auto keymasterSignFunction = [&](const std::string& to_be_signed) {
89 return key.sign(to_be_signed);
90 };
91 createSelfSignedCertificate(*publicKey, keymasterSignFunction, outPath);
92 return {};
Martijn Coenen95194842020-09-24 16:56:46 +020093}
94
95bool compileArtifacts(bool force) {
96 const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
97
98 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
99 0;
100}
101
102bool validateArtifacts() {
103 const char* const argv[] = {kOdrefreshPath, "--check"};
104
105 return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
106 0;
107}
108
Martijn Coenen5588e492021-02-25 14:33:44 +0100109static std::string toHex(const std::vector<uint8_t>& digest) {
110 std::stringstream ss;
111 for (auto it = digest.begin(); it != digest.end(); ++it) {
112 ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
113 }
114 return ss.str();
115}
116
117Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
118 std::error_code ec;
119 std::map<std::string, std::string> digests;
120
121 auto it = std::filesystem::recursive_directory_iterator(path, ec);
122 auto end = std::filesystem::recursive_directory_iterator();
123
124 while (!ec && it != end) {
125 if (it->is_regular_file()) {
126 auto digest = createDigest(it->path());
127 if (!digest.ok()) {
128 return Error() << "Failed to compute digest for " << it->path();
129 }
130 digests[it->path()] = toHex(*digest);
131 }
132 ++it;
133 }
134 if (ec) {
135 return Error() << "Failed to iterate " << path << ": " << ec;
136 }
137
138 return digests;
139}
140
141Result<void> verifyDigests(const std::map<std::string, std::string>& digests,
142 const std::map<std::string, std::string>& trusted_digests) {
143 for (const auto& path_digest : digests) {
144 auto path = path_digest.first;
145 auto digest = path_digest.second;
146 if ((trusted_digests.count(path) == 0)) {
147 return Error() << "Couldn't find digest for " << path;
148 }
149 if (trusted_digests.at(path) != digest) {
150 return Error() << "Digest mismatch for " << path;
151 }
152 }
153
154 // All digests matched!
155 if (digests.size() > 0) {
156 LOG(INFO) << "All root hashes match.";
157 }
158 return {};
159}
160
161Result<void> verifyIntegrityFsVerity(const std::map<std::string, std::string>& trusted_digests) {
162 // Just verify that the files are in verity, and get their digests
163 auto result = verifyAllFilesInVerity(kArtArtifactsDir);
164 if (!result.ok()) {
165 return result.error();
166 }
167
168 return verifyDigests(*result, trusted_digests);
169}
170
171Result<void> verifyIntegrityNoFsVerity(const std::map<std::string, std::string>& trusted_digests) {
172 // On these devices, just compute the digests, and verify they match the ones we trust
173 auto result = computeDigests(kArtArtifactsDir);
174 if (!result.ok()) {
175 return result.error();
176 }
177
178 return verifyDigests(*result, trusted_digests);
179}
180
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100181Result<OdsignInfo> getOdsignInfo(const SigningKey& /* key */) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100182 std::string persistedSignature;
183 OdsignInfo odsignInfo;
184
185 if (!android::base::ReadFileToString(kOdsignInfoSignature, &persistedSignature)) {
186 return ErrnoError() << "Failed to read " << kOdsignInfoSignature;
187 }
188
189 std::fstream odsign_info(kOdsignInfo, std::ios::in | std::ios::binary);
190 if (!odsign_info) {
191 return Error() << "Failed to open " << kOdsignInfo;
192 }
193 // Verify the hash
194 std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
195 std::istreambuf_iterator<char>());
196
197 // TODO: this is way too slow - replace with a local RSA_verify implementation.
198 /*
199 auto verifiedSignature = key.sign(odsign_info_str);
200 if (!verifiedSignature.ok()) {
201 return Error() << "Failed to sign " << kOdsignInfo;
202 }
203
204 if (*verifiedSignature != persistedSignature) {
205 return Error() << kOdsignInfoSignature << " does not match.";
206 } else {
207 LOG(INFO) << kOdsignInfoSignature << " matches.";
208 }
209 */
210
211 if (!odsignInfo.ParseFromIstream(&odsign_info)) {
212 return Error() << "Failed to parse " << kOdsignInfo;
213 }
214
215 LOG(INFO) << "Loaded " << kOdsignInfo;
216 return odsignInfo;
217}
218
219Result<void> persistDigests(const std::map<std::string, std::string>& digests,
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100220 const SigningKey& key) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100221 OdsignInfo signInfo;
222 google::protobuf::Map<std::string, std::string> proto_hashes(digests.begin(), digests.end());
223 auto map = signInfo.mutable_file_hashes();
224 *map = proto_hashes;
225
226 std::fstream odsign_info(kOdsignInfo, std::ios::out | std::ios::trunc | std::ios::binary);
227 if (!signInfo.SerializeToOstream(&odsign_info)) {
228 return Error() << "Failed to persist root hashes in " << kOdsignInfo;
229 }
230
231 odsign_info.seekg(0);
232 std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
233 std::istreambuf_iterator<char>());
234 auto signResult = key.sign(odsign_info_str);
235 if (!signResult.ok()) {
236 return Error() << "Failed to sign " << kOdsignInfo;
237 }
238 // Sign the files with our key itself, and write that to storage
239 android::base::WriteStringToFile(*signResult, kOdsignInfoSignature);
240 return {};
241}
242
Martijn Coenen95194842020-09-24 16:56:46 +0200243int main(int /* argc */, char** /* argv */) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100244 auto removeArtifacts = []() -> std::uintmax_t {
Martijn Coenen95194842020-09-24 16:56:46 +0200245 std::error_code ec;
246 auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
247 if (ec) {
248 // TODO can't remove artifacts, signal Zygote shouldn't use them
249 LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
Martijn Coenen5588e492021-02-25 14:33:44 +0100250 return 0;
Martijn Coenen95194842020-09-24 16:56:46 +0200251 } else {
Martijn Coenen5588e492021-02-25 14:33:44 +0100252 if (num_removed > 0) {
253 LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
254 }
255 return num_removed;
Martijn Coenen95194842020-09-24 16:56:46 +0200256 }
257 };
258 // Make sure we delete the artifacts in all early (error) exit paths
259 auto scope_guard = android::base::make_scope_guard(removeArtifacts);
260
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100261 SigningKey* key;
262 if (kUseKeystore) {
263 auto keystoreResult = KeystoreKey::getInstance();
264 if (!keystoreResult.ok()) {
265 LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200266 return -1;
267 }
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100268 key = keystoreResult.value();
Martijn Coenen95194842020-09-24 16:56:46 +0200269 } else {
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100270 // TODO - keymaster will go away
271 auto keymasterResult = KeymasterSigningKey::getInstance();
272 if (!keymasterResult.ok()) {
273 LOG(ERROR) << "Failed to create keymaster key: " << keymasterResult.error().message();
274 return -1;
275 }
276 key = keymasterResult.value();
Martijn Coenen95194842020-09-24 16:56:46 +0200277 }
278
Martijn Coenen5588e492021-02-25 14:33:44 +0100279 bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
280 if (!supportsFsVerity) {
281 LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
282 }
Martijn Coenen95194842020-09-24 16:56:46 +0200283
Martijn Coenen5588e492021-02-25 14:33:44 +0100284 if (supportsFsVerity) {
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100285 auto existing_cert = verifyExistingCert(*key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100286 if (!existing_cert.ok()) {
287 LOG(WARNING) << existing_cert.error().message();
288
289 // Try to create a new cert
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100290 auto new_cert = createX509Cert(*key, kSigningKeyCert);
Martijn Coenen5588e492021-02-25 14:33:44 +0100291 if (!new_cert.ok()) {
292 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
293 // TODO apparently the key become invalid - delete the blob / cert
294 return -1;
295 }
296 } else {
297 LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
298 }
299 auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
300 if (!cert_add_result.ok()) {
301 LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
302 << cert_add_result.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200303 return -1;
304 }
Martijn Coenen5588e492021-02-25 14:33:44 +0100305 }
306
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100307 auto signInfo = getOdsignInfo(*key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100308 if (!signInfo.ok()) {
309 int num_removed = removeArtifacts();
310 // Only a warning if there were artifacts to begin with, which suggests tampering or
311 // corruption
312 if (num_removed > 0) {
313 LOG(WARNING) << signInfo.error().message();
314 }
Martijn Coenen95194842020-09-24 16:56:46 +0200315 } else {
Martijn Coenen5588e492021-02-25 14:33:44 +0100316 std::map<std::string, std::string> trusted_digests(signInfo->file_hashes().begin(),
317 signInfo->file_hashes().end());
318 Result<void> integrityStatus;
319
320 if (supportsFsVerity) {
321 integrityStatus = verifyIntegrityFsVerity(trusted_digests);
322 } else {
323 integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
324 }
325 if (!integrityStatus.ok()) {
326 LOG(WARNING) << integrityStatus.error().message() << ", removing " << kArtArtifactsDir;
327 removeArtifacts();
328 }
Martijn Coenen799ce6e2021-02-23 10:14:41 +0100329 }
Martijn Coenen95194842020-09-24 16:56:46 +0200330
Martijn Coenen5588e492021-02-25 14:33:44 +0100331 // Ask ART whether it considers the artifacts valid
332 LOG(INFO) << "Asking odrefresh to verify artifacts (if present)...";
Martijn Coenen95194842020-09-24 16:56:46 +0200333 bool artifactsValid = validateArtifacts();
Martijn Coenen5588e492021-02-25 14:33:44 +0100334 LOG(INFO) << "odrefresh said they are " << (artifactsValid ? "VALID" : "INVALID");
Martijn Coenen95194842020-09-24 16:56:46 +0200335
336 if (!artifactsValid || kForceCompilation) {
Martijn Coenen95194842020-09-24 16:56:46 +0200337 LOG(INFO) << "Starting compilation... ";
338 bool ret = compileArtifacts(kForceCompilation);
339 LOG(INFO) << "Compilation done, returned " << ret;
340
Martijn Coenen5588e492021-02-25 14:33:44 +0100341 Result<std::map<std::string, std::string>> digests;
342 if (supportsFsVerity) {
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100343 digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100344 } else {
345 // If we can't use verity, just compute the root hashes and store
346 // those, so we can reverify them at the next boot.
347 digests = computeDigests(kArtArtifactsDir);
348 }
349 if (!digests.ok()) {
350 LOG(ERROR) << digests.error().message();
351 return -1;
352 }
Martijn Coenen95194842020-09-24 16:56:46 +0200353
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100354 auto persistStatus = persistDigests(*digests, *key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100355 if (!persistStatus.ok()) {
356 LOG(ERROR) << persistStatus.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200357 return -1;
358 }
359 }
360
361 // TODO we want to make sure Zygote only picks up the artifacts if we deemed
362 // everything was ok here. We could use a sysprop, or some other mechanism?
363 LOG(INFO) << "On-device signing done.";
364
365 scope_guard.Disable();
366 return 0;
367}