blob: eeef86843fed4af310edb181e7be225f7dcf63f2 [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 Coenendc05bb32021-03-08 10:52:48 +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 }
Martijn Coenendc05bb32021-03-08 10:52:48 +0100193 odsign_info.seekg(0);
Martijn Coenen5588e492021-02-25 14:33:44 +0100194 // Verify the hash
195 std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
196 std::istreambuf_iterator<char>());
197
Martijn Coenendc05bb32021-03-08 10:52:48 +0100198 auto publicKey = key.getPublicKey();
199 auto signResult = verifySignature(odsign_info_str, persistedSignature, *publicKey);
200 if (!signResult.ok()) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100201 return Error() << kOdsignInfoSignature << " does not match.";
202 } else {
203 LOG(INFO) << kOdsignInfoSignature << " matches.";
204 }
Martijn Coenen5588e492021-02-25 14:33:44 +0100205
Martijn Coenendc05bb32021-03-08 10:52:48 +0100206 odsign_info.seekg(0);
Martijn Coenen5588e492021-02-25 14:33:44 +0100207 if (!odsignInfo.ParseFromIstream(&odsign_info)) {
208 return Error() << "Failed to parse " << kOdsignInfo;
209 }
210
211 LOG(INFO) << "Loaded " << kOdsignInfo;
212 return odsignInfo;
213}
214
215Result<void> persistDigests(const std::map<std::string, std::string>& digests,
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100216 const SigningKey& key) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100217 OdsignInfo signInfo;
218 google::protobuf::Map<std::string, std::string> proto_hashes(digests.begin(), digests.end());
219 auto map = signInfo.mutable_file_hashes();
220 *map = proto_hashes;
221
Martijn Coenendc05bb32021-03-08 10:52:48 +0100222 std::fstream odsign_info(kOdsignInfo,
223 std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
Martijn Coenen5588e492021-02-25 14:33:44 +0100224 if (!signInfo.SerializeToOstream(&odsign_info)) {
225 return Error() << "Failed to persist root hashes in " << kOdsignInfo;
226 }
227
Martijn Coenendc05bb32021-03-08 10:52:48 +0100228 // Sign the signatures with our key itself, and write that to storage
229 odsign_info.seekg(0, std::ios::beg);
Martijn Coenen5588e492021-02-25 14:33:44 +0100230 std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
231 std::istreambuf_iterator<char>());
232 auto signResult = key.sign(odsign_info_str);
233 if (!signResult.ok()) {
234 return Error() << "Failed to sign " << kOdsignInfo;
235 }
Martijn Coenen5588e492021-02-25 14:33:44 +0100236 android::base::WriteStringToFile(*signResult, kOdsignInfoSignature);
237 return {};
238}
239
Martijn Coenen95194842020-09-24 16:56:46 +0200240int main(int /* argc */, char** /* argv */) {
Martijn Coenen5588e492021-02-25 14:33:44 +0100241 auto removeArtifacts = []() -> std::uintmax_t {
Martijn Coenen95194842020-09-24 16:56:46 +0200242 std::error_code ec;
243 auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
244 if (ec) {
245 // TODO can't remove artifacts, signal Zygote shouldn't use them
246 LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
Martijn Coenen5588e492021-02-25 14:33:44 +0100247 return 0;
Martijn Coenen95194842020-09-24 16:56:46 +0200248 } else {
Martijn Coenen5588e492021-02-25 14:33:44 +0100249 if (num_removed > 0) {
250 LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
251 }
252 return num_removed;
Martijn Coenen95194842020-09-24 16:56:46 +0200253 }
254 };
255 // Make sure we delete the artifacts in all early (error) exit paths
256 auto scope_guard = android::base::make_scope_guard(removeArtifacts);
257
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100258 SigningKey* key;
259 if (kUseKeystore) {
260 auto keystoreResult = KeystoreKey::getInstance();
261 if (!keystoreResult.ok()) {
262 LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200263 return -1;
264 }
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100265 key = keystoreResult.value();
Martijn Coenen95194842020-09-24 16:56:46 +0200266 } else {
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100267 // TODO - keymaster will go away
268 auto keymasterResult = KeymasterSigningKey::getInstance();
269 if (!keymasterResult.ok()) {
270 LOG(ERROR) << "Failed to create keymaster key: " << keymasterResult.error().message();
271 return -1;
272 }
273 key = keymasterResult.value();
Martijn Coenen95194842020-09-24 16:56:46 +0200274 }
275
Martijn Coenen5588e492021-02-25 14:33:44 +0100276 bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
277 if (!supportsFsVerity) {
278 LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
279 }
Martijn Coenen95194842020-09-24 16:56:46 +0200280
Martijn Coenen5588e492021-02-25 14:33:44 +0100281 if (supportsFsVerity) {
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100282 auto existing_cert = verifyExistingCert(*key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100283 if (!existing_cert.ok()) {
284 LOG(WARNING) << existing_cert.error().message();
285
286 // Try to create a new cert
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100287 auto new_cert = createX509Cert(*key, kSigningKeyCert);
Martijn Coenen5588e492021-02-25 14:33:44 +0100288 if (!new_cert.ok()) {
289 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
290 // TODO apparently the key become invalid - delete the blob / cert
291 return -1;
292 }
293 } else {
294 LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
295 }
296 auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
297 if (!cert_add_result.ok()) {
298 LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
299 << cert_add_result.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200300 return -1;
301 }
Martijn Coenen5588e492021-02-25 14:33:44 +0100302 }
303
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100304 auto signInfo = getOdsignInfo(*key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100305 if (!signInfo.ok()) {
306 int num_removed = removeArtifacts();
307 // Only a warning if there were artifacts to begin with, which suggests tampering or
308 // corruption
309 if (num_removed > 0) {
310 LOG(WARNING) << signInfo.error().message();
311 }
Martijn Coenen95194842020-09-24 16:56:46 +0200312 } else {
Martijn Coenen5588e492021-02-25 14:33:44 +0100313 std::map<std::string, std::string> trusted_digests(signInfo->file_hashes().begin(),
314 signInfo->file_hashes().end());
315 Result<void> integrityStatus;
316
317 if (supportsFsVerity) {
318 integrityStatus = verifyIntegrityFsVerity(trusted_digests);
319 } else {
320 integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
321 }
322 if (!integrityStatus.ok()) {
323 LOG(WARNING) << integrityStatus.error().message() << ", removing " << kArtArtifactsDir;
324 removeArtifacts();
325 }
Martijn Coenen799ce6e2021-02-23 10:14:41 +0100326 }
Martijn Coenen95194842020-09-24 16:56:46 +0200327
Martijn Coenen5588e492021-02-25 14:33:44 +0100328 // Ask ART whether it considers the artifacts valid
329 LOG(INFO) << "Asking odrefresh to verify artifacts (if present)...";
Martijn Coenen95194842020-09-24 16:56:46 +0200330 bool artifactsValid = validateArtifacts();
Martijn Coenen5588e492021-02-25 14:33:44 +0100331 LOG(INFO) << "odrefresh said they are " << (artifactsValid ? "VALID" : "INVALID");
Martijn Coenen95194842020-09-24 16:56:46 +0200332
333 if (!artifactsValid || kForceCompilation) {
Martijn Coenen95194842020-09-24 16:56:46 +0200334 LOG(INFO) << "Starting compilation... ";
335 bool ret = compileArtifacts(kForceCompilation);
336 LOG(INFO) << "Compilation done, returned " << ret;
337
Martijn Coenen5588e492021-02-25 14:33:44 +0100338 Result<std::map<std::string, std::string>> digests;
339 if (supportsFsVerity) {
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100340 digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100341 } else {
342 // If we can't use verity, just compute the root hashes and store
343 // those, so we can reverify them at the next boot.
344 digests = computeDigests(kArtArtifactsDir);
345 }
346 if (!digests.ok()) {
347 LOG(ERROR) << digests.error().message();
348 return -1;
349 }
Martijn Coenen95194842020-09-24 16:56:46 +0200350
Martijn Coenenba1c9dc2021-02-04 13:18:29 +0100351 auto persistStatus = persistDigests(*digests, *key);
Martijn Coenen5588e492021-02-25 14:33:44 +0100352 if (!persistStatus.ok()) {
353 LOG(ERROR) << persistStatus.error().message();
Martijn Coenen95194842020-09-24 16:56:46 +0200354 return -1;
355 }
356 }
357
358 // TODO we want to make sure Zygote only picks up the artifacts if we deemed
359 // everything was ok here. We could use a sysprop, or some other mechanism?
360 LOG(INFO) << "On-device signing done.";
361
362 scope_guard.Disable();
363 return 0;
364}