diff --git a/Decrypt.cpp b/Decrypt.cpp index a02bad89..5029e0dd 100755 --- a/Decrypt.cpp +++ b/Decrypt.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "cutils/properties.h" @@ -296,18 +297,17 @@ bool Get_Spblob_Data(const std::string& spblob_path, const std::string& handle_s } else found_file = true; } else { + // The Keystore alias uses the handle as-is, but the spblob files are + // named after it zero-padded to 16 digits. printf("trying to read %s_file data with leading 0\n", tag.c_str()); - std::vector file_paths = { - spblob_path + "0" + handle_str + suffix, - spblob_path + "00" + handle_str + suffix - }; - for (auto& file : file_paths) { - if (!android::base::ReadFileToString(file, data)) { - printf("Failed to read '%s'\n", file.c_str()); - } else { - found_file = true; - break; - } + std::string padded = handle_str.size() < 16 + ? std::string(16 - handle_str.size(), '0') + handle_str + : handle_str; + file = spblob_path + padded + suffix; + if (!android::base::ReadFileToString(file, data)) { + printf("Failed to read '%s'\n", file.c_str()); + } else { + found_file = true; } } return found_file; @@ -491,16 +491,57 @@ namespace keystore { } } - void copySqliteDb() { - std::string keystore_path = "/tmp/misc/keystore/"; - std::string dst = keystore_path + "persistent.sqlite"; - std::string src = "/data/misc/keystore/persistent.sqlite"; - std::ifstream srcif(src.c_str(), std::ios::binary); - std::ofstream dstof(dst.c_str(), std::ios::binary); - printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str()); - dstof << srcif.rdbuf(); - srcif.close(); - dstof.close(); + static bool waitForServiceState(const char* service, const char* state) { + char current[PROPERTY_VALUE_MAX] = {}; + std::string prop = std::string("init.svc.") + service; + for (int i = 0; i < 100; i++) { + property_get(prop.c_str(), current, ""); + if (!strcmp(current, state)) return true; + usleep(100000); + } + printf("'%s' did not reach state '%s'\n", service, state); + return false; + } + + /* keystore2 runs with its database on tmpfs so that nothing done here can + * reach the real one. It still has to see the installed system's keys, so + * seed it from /data before the first keystore2 call. */ + bool syncKeystoreDb() { + static bool synced = false; + if (synced) return true; + + const std::string src = "/data/misc/keystore/persistent.sqlite"; + const std::string dst = "/tmp/misc/keystore/persistent.sqlite"; + if (!android::vold::pathExists(src)) { + printf("no keystore database at '%s'\n", src.c_str()); + return false; + } + + // keystore2 holds the destination open, so stop it for a clean copy. + printf("stopping keystore2 to sync '%s'\n", src.c_str()); + property_set("ctl.stop", "keystore2"); + waitForServiceState("keystore2", "stopped"); + + unlink("/tmp/misc/keystore/persistent.sqlite-wal"); + unlink("/tmp/misc/keystore/persistent.sqlite-shm"); + unlink(dst.c_str()); + + KeystoreInfo keystore_info; + bool copied = keystore_info.backupDatabase(src, dst); + if (copied) chmod(dst.c_str(), 0600); + + property_set("ctl.start", "keystore2"); + if (!waitForServiceState("keystore2", "running")) return false; + for (int i = 0; i < 100; i++) { + if (AServiceManager_checkService( + "android.system.keystore2.IKeystoreService/default") != nullptr) { + synced = copied; + return copied; + } + usleep(100000); + } + printf("keystore2 did not register after restart\n"); + return false; } /* C++ replacement for function of the same name @@ -591,6 +632,25 @@ namespace keystore { printf("Begin Operation failed\n"); return disk_decryption_secret_key; } + if (encOperationResponse.upgradedBlob) { + /* KeyMint rebound the synthetic password key because our OS + * version or patch levels do not match the installed system. + * The upgrade only lives in the tmpfs Keystore database, so the + * blob on /data stays usable, but keystore2 has marked the old + * one superseded and its garbage collector deletes superseded + * blobs from KeyMint itself. That collector is gated on + * sys.boot_completed, so it stays idle here unless something + * sets that property. */ + printf("WARNING: KeyMint upgraded the synthetic password key\n"); + printf("WARNING: the recovery environment does not match the installed system\n"); + char boot_completed[PROPERTY_VALUE_MAX] = {}; + property_get("sys.boot_completed", boot_completed, ""); + if (!strcmp(boot_completed, "1")) { + printf("ERROR: sys.boot_completed is set, so keystore2 will garbage\n"); + printf("ERROR: collect the original key blob. Do not set that property\n"); + printf("ERROR: in recovery.\n"); + } + } std::optional> optPlaintext; begin_rc = encOperationResponse.iOperation->finish(cipher_text_hidlvec, {}, &optPlaintext); @@ -734,6 +794,12 @@ bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password // Get the handle: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017 KeystoreInfo keystore_info; std::string handle_str = keystore_info.getHandle(user_id); + // The synthetic password key is looked up by alias, so keystore2 needs the + // installed system's database before any of the calls below. + if (!android::keystore::syncKeystoreDb()) { + printf("Failed to sync the keystore database\n"); + return Free_Return(retval, weaver_key, &pwd); + } // Now we begin driving unwrapPasswordBasedSyntheticPassword from: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#758 // First we read the password data which contains scrypt parameters // printf("pwd N %i R %i P %i salt ", pwd.scryptN, pwd.scryptR, pwd.scryptP); output_hex((char*)pwd.salt, pwd.salt_len); printf("\n"); @@ -751,7 +817,6 @@ bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password return Free_Return(retval, weaver_key, &pwd); } } else { - android::keystore::copySqliteDb(); // early copy db for keystore std::string defpassword = "default-password"; memcpy(password_token, defpassword.data(), defpassword.length()); } @@ -1018,9 +1083,9 @@ extern "C" int Get_Password_Type(const userid_t user_id, std::string& filename) extern "C" bool Decrypt_User(const userid_t user_id, const std::string& Password) { printf("Attempting to decrypt user\n"); - uint8_t *auth_token; - uint32_t auth_token_len; - int ret; + uint8_t *auth_token = NULL; + uint32_t auth_token_len = 0; + int ret = -1; struct stat st; if (user_id > 9999) { @@ -1056,15 +1121,35 @@ extern "C" bool Decrypt_User(const userid_t user_id, const std::string& Password } bool should_reenroll; bool request_reenroll = false; - android::sp gk_device; - gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService(); - if (gk_device == nullptr) - return false; android::hardware::hidl_vec curPwdHandle; curPwdHandle.setToExternal(const_cast((const uint8_t *)handle.c_str()), st.st_size); android::hardware::hidl_vec enteredPwd; enteredPwd.setToExternal(const_cast((const uint8_t *)Password.c_str()), Password.size()); + // Devices that only declare the AIDL GateKeeper have no HIDL service. + constexpr const char gatekeeperServiceName[] = "android.hardware.gatekeeper.IGatekeeper/default"; + if (AServiceManager_isDeclared(gatekeeperServiceName)) { + ::ndk::SpAIBinder gkBinder(AServiceManager_waitForService(gatekeeperServiceName)); + auto aidl_gk_device = AidlIGatekeeper::fromBinder(gkBinder); + if (!aidl_gk_device) { + printf("failed to get gatekeeper service\n"); + return false; + } + AidlGatekeeperVerifyResp rsp; + auto result = aidl_gk_device->verify(user_id, 0 /* challenge */, curPwdHandle, enteredPwd, &rsp); + if (!result.isOk() || rsp.statusCode < AidlIGatekeeper::STATUS_OK) { + printf("gatekeeper verification failed\n"); + return false; + } + printf("GateKeeper status ok\n"); + std::string secret = HashPassword(Password); + return Decrypt_CE_storage(user_id, secret); + } + + android::sp gk_device; + gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService(); + if (gk_device == nullptr) + return false; android::hardware::Return hwRet = @@ -1089,16 +1174,12 @@ extern "C" bool Decrypt_User(const userid_t user_id, const std::string& Password } } ); - if (!hwRet.isOk()) { + delete[] auth_token; + if (!hwRet.isOk() || ret != 0) { + printf("gatekeeper verification failed\n"); return false; } - char token_hex[(auth_token_len*2)+1]; - token_hex[(auth_token_len*2)] = 0; - uint32_t i; - for (i=0;i lock(key_upgrade_lock); @@ -360,17 +356,15 @@ static KeystoreOperation BeginKeystoreOp(Keystore& keystore, const std::string& if (!opHandle) return opHandle; // If key blob wasn't upgraded, nothing left to do. - // if (!opHandle.getUpgradedBlob()) return opHandle; + if (!opHandle.getUpgradedBlob()) return opHandle; // if (already_upgraded) { // LOG(ERROR) << "Unexpected case; already-upgraded key " << upgraded_blob_file // << " still requires upgrade"; // return KeystoreOperation(); // } - LOG(INFO) << "Upgrading key: " << blob_file; - - if (!writeStringToFile(*opHandle.getUpgradedBlob(), upgraded_blob_file)) - return KeystoreOperation(); + LOG(WARNING) << "KeyMint upgraded " << blob_file + << " for this operation only; the on-disk blob is left unchanged"; // if (cp_needsCheckpoint()) { // LOG(INFO) << "Wrote upgraded key to " << upgraded_blob_file // << "; delaying commit due to checkpoint"; diff --git a/Keystore.cpp b/Keystore.cpp index d7b1a0f2..24b78ef2 100644 --- a/Keystore.cpp +++ b/Keystore.cpp @@ -16,6 +16,8 @@ #include "Keystore.h" +#include + #include #include @@ -103,8 +105,22 @@ bool KeystoreOperation::finish(std::string* output) { return true; } +// AServiceManager_waitForService() waits forever, which hangs the decrypt UI +// when a service cannot come up at all. Poll so the caller can report failure, +// but leave enough room for a device that starts keystore2 or the KeyMint HAL +// from a script partway through decryption. +static ::ndk::SpAIBinder waitForService(const char* name) { + for (int i = 0; i < 300; i++) { + ::ndk::SpAIBinder binder(AServiceManager_checkService(name)); + if (binder.get() != nullptr) return binder; + usleep(100000); + } + LOG(ERROR) << "Timed out waiting for " << name; + return ::ndk::SpAIBinder(); +} + Keystore::Keystore() { - ::ndk::SpAIBinder binder(AServiceManager_waitForService(keystore2_service_name)); + ::ndk::SpAIBinder binder(waitForService(keystore2_service_name)); auto keystore2Service = ks2::IKeystoreService::fromBinder(binder); if (!keystore2Service) { @@ -224,7 +240,7 @@ KeystoreOperation Keystore::begin(const std::string& key, const km::Authorizatio } void Keystore::earlyBootEnded() { - ::ndk::SpAIBinder binder(AServiceManager_waitForService(maintenance_service_name)); + ::ndk::SpAIBinder binder(waitForService(maintenance_service_name)); auto maint_service = ks2_maint::IKeystoreMaintenance::fromBinder(binder); if (!maint_service) { @@ -237,7 +253,7 @@ void Keystore::earlyBootEnded() { } void Keystore::deleteAllKeys() { - ::ndk::SpAIBinder binder(AServiceManager_waitForService(maintenance_service_name)); + ::ndk::SpAIBinder binder(waitForService(maintenance_service_name)); auto maint_service = ks2_maint::IKeystoreMaintenance::fromBinder(binder); if (!maint_service) { diff --git a/KeystoreInfo.cpp b/KeystoreInfo.cpp index a74a713f..04a6811e 100755 --- a/KeystoreInfo.cpp +++ b/KeystoreInfo.cpp @@ -53,6 +53,7 @@ std::string KeystoreInfo::getHandle(const userid_t user_id) { rc = sqlite3_open("/data/system/locksettings.db", &db); if (rc) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); return ""; } std::string sql = "SELECT * FROM locksettings WHERE name = 'sp-handle' AND user = " + std::to_string(user_id); @@ -67,6 +68,8 @@ std::string KeystoreInfo::getHandle(const userid_t user_id) { } if (rc != SQLITE_DONE) { fprintf(stderr, "error: %s\n", sqlite3_errmsg(db)); + sqlite3_finalize(stmt); + sqlite3_close(db); return ""; } sqlite3_finalize(stmt); @@ -74,6 +77,28 @@ std::string KeystoreInfo::getHandle(const userid_t user_id) { return uint2hex(value); } +// Uses the SQLite backup API rather than a plain file copy so that a database +// with pending WAL content is reproduced correctly. +bool KeystoreInfo::backupDatabase(const std::string& src, const std::string& dst) { + sqlite3 *src_db = NULL; + sqlite3 *dst_db = NULL; + bool ok = false; + + if (sqlite3_open_v2(src.c_str(), &src_db, SQLITE_OPEN_READONLY, NULL) == SQLITE_OK && + sqlite3_open(dst.c_str(), &dst_db) == SQLITE_OK) { + sqlite3_backup *backup = sqlite3_backup_init(dst_db, "main", src_db, "main"); + if (backup) { + sqlite3_backup_step(backup, -1); + ok = sqlite3_backup_finish(backup) == SQLITE_OK; + } + } + if (!ok) + fprintf(stderr, "Failed to back up '%s' to '%s'\n", src.c_str(), dst.c_str()); + sqlite3_close(dst_db); + sqlite3_close(src_db); + return ok; +} + std::string KeystoreInfo::getAlias(std::string handle) { std::string alias(SYNTHETIC_PASSWORD_KEY_PREFIX); alias = alias + handle; diff --git a/KeystoreInfo.hpp b/KeystoreInfo.hpp index a6a5850e..9c40b4a0 100755 --- a/KeystoreInfo.hpp +++ b/KeystoreInfo.hpp @@ -24,6 +24,7 @@ class KeystoreInfo { public: std::string getHandle(const userid_t user_id); std::string getAlias(std::string handle); +bool backupDatabase(const std::string& src, const std::string& dst); private: std::string uint2hex(int64_t num); diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp index 3842e8fa..bc2e7ea3 100644 --- a/MetadataCrypt.cpp +++ b/MetadataCrypt.cpp @@ -130,15 +130,9 @@ static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& g auto in_dsu = android::base::GetBoolProperty("ro.gsid.image_running", false); // !pathExists(dir) does not imply there's a factory reset when in DSU mode. if (!pathExists(dir) && !in_dsu && first_key) { - auto delete_all = android::base::GetBoolProperty( - "ro.crypto.metadata_init_delete_all_keys.enabled", false); - if (delete_all) { - LOG(INFO) << "Metadata key does not exist, calling deleteAllKeys"; - Keystore::deleteAllKeys(); - } else { - LOG(INFO) << "Metadata key does not exist but " - "ro.crypto.metadata_init_delete_all_keys.enabled is false"; - } + // AOSP wipes Keystore here, assuming a missing key means a factory + // reset. In recovery it usually means /metadata failed to mount. + LOG(WARNING) << "Metadata key does not exist at " << dir << ", not wiping Keystore"; } auto temp = metadata_key_dir + "/tmp"; return retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key); diff --git a/Utils.cpp b/Utils.cpp index 4e2d221f..d55ebaf1 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -66,6 +66,11 @@ using android::base::StartsWith; using android::base::StringPrintf; using android::base::unique_fd; +// Declared by sehandle.h at global scope. AOSP has each executable define and +// initialize this in its own main(); it lives here instead so that the ones +// that link libvold without a main() of their own still get a handle. +struct selabel_handle* sehandle; + namespace android { namespace vold { @@ -89,12 +94,24 @@ static const char* kAppObbDir = "/Android/obb/"; static const char* kMediaProviderCtx = "u:r:mediaprovider:"; static const char* kMediaProviderAppCtx = "u:r:mediaprovider_app:"; -struct selabel_handle* sehandle; // Lock used to protect process-level SELinux changes from racing with each // other between multiple threads. static std::mutex kSecurityLock; +struct selabel_handle* GetSehandle() { + static std::once_flag once; + std::call_once(once, [] { + sehandle = selinux_android_file_context_handle(); + if (!sehandle) { + LOG(ERROR) << "Failed to get SELinux file contexts handle"; + return; + } + selinux_android_set_sehandle(sehandle); + }); + return sehandle; +} + std::string GetFuseMountPathForUser(userid_t user_id, const std::string& relative_upper_path) { return StringPrintf("/mnt/user/%d/%s", user_id, relative_upper_path.c_str()); } @@ -106,7 +123,10 @@ status_t CreateDeviceNode(const std::string& path, dev_t dev) { auto secontext = std::unique_ptr(nullptr, freecon); char* tmp_secontext; - if (selabel_lookup(sehandle, &tmp_secontext, cpath, S_IFBLK) == 0) { + auto* handle = GetSehandle(); + if (!handle) { + LOG(WARNING) << "No file_contexts, " << path << " will inherit its parent's label"; + } else if (selabel_lookup(handle, &tmp_secontext, cpath, S_IFBLK) == 0) { secontext.reset(tmp_secontext); if (setfscreatecon(secontext.get()) != 0) { LOG(ERROR) << "Failed to setfscreatecon for device node " << path; @@ -456,20 +476,22 @@ status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, auto clearfscreatecon = android::base::make_scope_guard([] { setfscreatecon(nullptr); }); auto secontext = std::unique_ptr(nullptr, freecon); char* tmp_secontext; -// if (selabel_lookup(sehandle, &tmp_secontext, cpath, S_IFDIR) == 0) { -// LOG(INFO) << "PrepareDir selabel_lookup"; -// secontext.reset(tmp_secontext); -// LOG(INFO) << "PrepareDir secontext reset"; -// if (setfscreatecon(secontext.get()) != 0) { -// LOG(ERROR) << "Failed to setfscreatecon for directory " << path; -// return -EINVAL; -// } -// } else if (errno == ENOENT) { -// LOG(INFO) << "No selabel defined for directory " << path; -// } else { -// LOG(ERROR) << "Failed to look up selabel for directory " << path; -// return -errno; -// } + + auto* handle = GetSehandle(); + if (!handle) { + LOG(WARNING) << "No file_contexts, " << path << " will inherit its parent's label"; + } else if (selabel_lookup(handle, &tmp_secontext, cpath, S_IFDIR) == 0) { + secontext.reset(tmp_secontext); + if (setfscreatecon(secontext.get()) != 0) { + LOG(ERROR) << "Failed to setfscreatecon for directory " << path; + return -EINVAL; + } + } else if (errno == ENOENT) { + LOG(DEBUG) << "No selabel defined for directory " << path; + } else { + PLOG(ERROR) << "Failed to look up selabel for directory " << path; + return -errno; + } if (fs_prepare_dir(cpath, mode, uid, gid) != 0) return -errno; if (attrs && SetAttrs(path, attrs) != 0) return -errno; @@ -1244,12 +1266,12 @@ bool IsSameFile(const std::string& path1, const std::string& path2) { status_t RestoreconRecursive(const std::string& path) { LOG(DEBUG) << "Starting restorecon of " << path; - static constexpr const char* kRestoreconString = "selinux.restorecon_recursive"; - - android::base::SetProperty(kRestoreconString, ""); - android::base::SetProperty(kRestoreconString, path); - - android::base::WaitForProperty(kRestoreconString, path); + // AOSP hands this to init through selinux.restorecon_recursive. Recovery's + // init has no trigger for that property, so relabel here instead. + if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { + PLOG(ERROR) << "Failed to restorecon " << path; + return -errno; + } LOG(DEBUG) << "Finished restorecon of " << path; return OK; diff --git a/main.cpp b/main.cpp index bdce76ed..ebb8754e 100644 --- a/main.cpp +++ b/main.cpp @@ -57,7 +57,6 @@ static void parse_args(int argc, char** argv); static void VoldLogger(android::base::LogId log_buffer_id, android::base::LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message); -struct selabel_handle* sehandle; android::base::LogdLogger logd_logger(android::base::SYSTEM); using android::base::StringPrintf; @@ -82,12 +81,7 @@ int main(int argc, char** argv) { parse_args(argc, argv); - sehandle = selinux_android_file_context_handle(); - if (!sehandle) { - LOG(ERROR) << "Failed to get SELinux file contexts handle"; - exit(1); - } - selinux_android_set_sehandle(sehandle); + if (!android::vold::GetSehandle()) exit(1); mkdir("/dev/block/vold", 0755); diff --git a/sehandle.h b/sehandle.h index 8921db5b..75748431 100644 --- a/sehandle.h +++ b/sehandle.h @@ -21,4 +21,16 @@ extern struct selabel_handle* sehandle; +namespace android { +namespace vold { + +/* Returns the file_contexts handle, opening it on first use. vold's main() + * calls this at startup; executables that link libvold without it, such as + * recovery, get the handle on their first PrepareDir(). Returns null when + * file_contexts could not be opened. */ +struct selabel_handle* GetSehandle(); + +} // namespace vold +} // namespace android + #endif diff --git a/tests/VoldFuzzer.cpp b/tests/VoldFuzzer.cpp index 173c7654..b0b93e92 100644 --- a/tests/VoldFuzzer.cpp +++ b/tests/VoldFuzzer.cpp @@ -24,15 +24,11 @@ using ::android::fuzzService; using ::android::sp; -struct selabel_handle* sehandle; - extern "C" int LLVMFuzzerInitialize(int argc, char argv) { - sehandle = selinux_android_file_context_handle(); - if (!sehandle) { + if (!android::vold::GetSehandle()) { LOG(ERROR) << "Failed to get SELinux file contexts handle in voldFuzzer!"; exit(1); } - selinux_android_set_sehandle(sehandle); return 0; }