From 275232667d26fd5d77dc3c2654fd02b043e82ebc Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Tue, 22 Oct 2019 12:30:39 -0700 Subject: [PATCH] adbd: use libadbd_auth for authentication. Bug: http://b/137798163 Test: for i in `seq 1 100000`; do echo $i; adb wait-for-device shell "su 0 stop; su 0 start; sleep 10"; adb disconnect; done Change-Id: Ie481e79a48c4aabf18ef797317ba18f207808c63 --- adb/Android.bp | 6 + adb/adb.cpp | 1 + adb/daemon/auth.cpp | 264 +++++++++++++------------------------------- adb/daemon/main.cpp | 5 +- adb/transport.h | 3 + 5 files changed, 87 insertions(+), 192 deletions(-) diff --git a/adb/Android.bp b/adb/Android.bp index d14fe56bb..9c7a1b936 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -361,6 +361,7 @@ cc_library_static { ], shared_libs: [ + "libadbd_auth", "libasyncio", "libbase", "libcrypto", @@ -414,6 +415,7 @@ cc_library { ], shared_libs: [ + "libadbd_auth", "libasyncio", "libbase", "libcrypto", @@ -460,6 +462,7 @@ cc_library { ], shared_libs: [ + "libadbd_auth", "libadbd_services", "libasyncio", "libbase", @@ -494,6 +497,7 @@ cc_binary { shared_libs: [ "libadbd", + "libadbd_auth", "libadbd_services", "libbase", "libcap", @@ -509,6 +513,7 @@ phony { name: "adbd_system_binaries", required: [ "abb", + "libadbd_auth", "reboot", "set-verity-state", ] @@ -615,6 +620,7 @@ cc_test { static_libs: [ "libadbd", + "libadbd_auth", "libbase", "libcutils", "libcrypto_utils", diff --git a/adb/adb.cpp b/adb/adb.cpp index 1ec145b25..9b663be91 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp @@ -300,6 +300,7 @@ static void handle_new_connection(atransport* t, apacket* p) { handle_online(t); #else if (!auth_required) { + LOG(INFO) << "authentication not required"; handle_online(t); send_connect(t); } else { diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp index 7a3a4f5c5..2e84ce6b9 100644 --- a/adb/daemon/auth.cpp +++ b/adb/daemon/auth.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -38,85 +39,57 @@ #include #include -static fdevent* listener_fde = nullptr; -static fdevent* framework_fde = nullptr; -static auto& framework_mutex = *new std::mutex(); -static int framework_fd GUARDED_BY(framework_mutex) = -1; -static auto& connected_keys GUARDED_BY(framework_mutex) = *new std::vector; +static AdbdAuthContext* auth_ctx; static void adb_disconnected(void* unused, atransport* t); static struct adisconnect adb_disconnect = {adb_disconnected, nullptr}; -static atransport* adb_transport; -static bool needs_retry = false; bool auth_required = true; -bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig, - std::string* auth_key) { - static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr }; - - for (const auto& path : key_paths) { - if (access(path, R_OK) == 0) { - LOG(INFO) << "Loading keys from " << path; - std::string content; - if (!android::base::ReadFileToString(path, &content)) { - PLOG(ERROR) << "Couldn't read " << path; - continue; - } - - for (const auto& line : android::base::Split(content, "\n")) { - if (line.empty()) continue; - *auth_key = line; - // TODO: do we really have to support both ' ' and '\t'? - char* sep = strpbrk(const_cast(line.c_str()), " \t"); - if (sep) *sep = '\0'; - - // b64_pton requires one additional byte in the target buffer for - // decoding to succeed. See http://b/28035006 for details. - uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1]; - if (b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) { - LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path; - continue; - } - - RSA* key = nullptr; - if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) { - LOG(ERROR) << "Failed to parse key " << line.c_str() << " in " << path; - continue; - } - - bool verified = - (RSA_verify(NID_sha1, reinterpret_cast(token), token_size, - reinterpret_cast(sig.c_str()), sig.size(), - key) == 1); - RSA_free(key); - if (verified) return true; - } - } - } - auth_key->clear(); - return false; +static void IteratePublicKeys(std::function f) { + adbd_auth_get_public_keys( + auth_ctx, + [](const char* public_key, size_t len, void* arg) { + return (*static_cast(arg))(std::string_view(public_key, len)); + }, + &f); } -static bool adbd_send_key_message_locked(std::string_view msg_type, std::string_view key) - REQUIRES(framework_mutex) { - if (framework_fd < 0) { - LOG(ERROR) << "Client not connected to send msg_type " << msg_type; - return false; - } - std::string msg = std::string(msg_type) + std::string(key); - int msg_len = msg.length(); - if (msg_len >= static_cast(MAX_FRAMEWORK_PAYLOAD)) { - LOG(ERROR) << "Key too long (" << msg_len << ")"; - return false; - } +bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig, + std::string* auth_key) { + bool authorized = false; + auth_key->clear(); - LOG(DEBUG) << "Sending '" << msg << "'"; - if (!WriteFdExactly(framework_fd, msg.c_str(), msg_len)) { - PLOG(ERROR) << "Failed to write " << msg_type; - return false; - } - return true; + IteratePublicKeys([&](std::string_view public_key) { + // TODO: do we really have to support both ' ' and '\t'? + std::vector split = android::base::Split(std::string(public_key), " \t"); + uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1]; + const std::string& pubkey = split[0]; + if (b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) { + LOG(ERROR) << "Invalid base64 key " << pubkey; + return true; + } + + RSA* key = nullptr; + if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) { + LOG(ERROR) << "Failed to parse key " << pubkey; + return true; + } + + bool verified = + (RSA_verify(NID_sha1, reinterpret_cast(token), token_size, + reinterpret_cast(sig.c_str()), sig.size(), key) == 1); + RSA_free(key); + if (verified) { + *auth_key = public_key; + authorized = true; + return false; + } + + return true; + }); + + return authorized; } static bool adbd_auth_generate_token(void* token, size_t token_size) { @@ -127,113 +100,6 @@ static bool adbd_auth_generate_token(void* token, size_t token_size) { return okay; } -static void adb_disconnected(void* unused, atransport* t) { - LOG(INFO) << "ADB disconnect"; - adb_transport = nullptr; - needs_retry = false; - { - std::lock_guard lock(framework_mutex); - if (framework_fd >= 0) { - adbd_send_key_message_locked("DC", t->auth_key); - } - connected_keys.erase(std::remove(connected_keys.begin(), connected_keys.end(), t->auth_key), - connected_keys.end()); - } -} - -static void framework_disconnected() { - LOG(INFO) << "Framework disconnect"; - if (framework_fde) { - fdevent_destroy(framework_fde); - { - std::lock_guard lock(framework_mutex); - framework_fd = -1; - } - } -} - -static void adbd_auth_event(int fd, unsigned events, void*) { - if (events & FDE_READ) { - char response[2]; - int ret = unix_read(fd, response, sizeof(response)); - if (ret <= 0) { - framework_disconnected(); - } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') { - if (adb_transport) { - adbd_auth_verified(adb_transport); - } - } - } -} - -void adbd_auth_confirm_key(atransport* t) { - if (!adb_transport) { - adb_transport = t; - t->AddDisconnect(&adb_disconnect); - } - - { - std::lock_guard lock(framework_mutex); - if (framework_fd < 0) { - LOG(ERROR) << "Client not connected"; - needs_retry = true; - return; - } - - adbd_send_key_message_locked("PK", t->auth_key); - } -} - -static void adbd_auth_listener(int fd, unsigned events, void* data) { - int s = adb_socket_accept(fd, nullptr, nullptr); - if (s < 0) { - PLOG(ERROR) << "Failed to accept"; - return; - } - - { - std::lock_guard lock(framework_mutex); - if (framework_fd >= 0) { - LOG(WARNING) << "adb received framework auth socket connection again"; - framework_disconnected(); - } - - framework_fd = s; - framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr); - fdevent_add(framework_fde, FDE_READ); - - if (needs_retry) { - needs_retry = false; - send_auth_request(adb_transport); - } - - // if a client connected before the framework was available notify the framework of the - // connected key now. - if (!connected_keys.empty()) { - for (const auto& key : connected_keys) { - adbd_send_key_message_locked("CK", key); - } - } - } -} - -void adbd_notify_framework_connected_key(atransport* t) { - if (!adb_transport) { - adb_transport = t; - t->AddDisconnect(&adb_disconnect); - } - { - std::lock_guard lock(framework_mutex); - if (std::find(connected_keys.begin(), connected_keys.end(), t->auth_key) == - connected_keys.end()) { - connected_keys.push_back(t->auth_key); - } - if (framework_fd >= 0) { - adbd_send_key_message_locked("CK", t->auth_key); - } - } -} - void adbd_cloexec_auth_socket() { int fd = android_get_control_socket("adbd"); if (fd == -1) { @@ -243,20 +109,23 @@ void adbd_cloexec_auth_socket() { fcntl(fd, F_SETFD, FD_CLOEXEC); } +static void adbd_auth_key_authorized(void* arg, uint64_t id) { + LOG(INFO) << "adb client authorized"; + auto* transport = static_cast(arg); + transport->auth_id = id; + adbd_auth_verified(transport); +} + void adbd_auth_init(void) { - int fd = android_get_control_socket("adbd"); - if (fd == -1) { - PLOG(ERROR) << "Failed to get adbd socket"; - return; - } - - if (listen(fd, 4) == -1) { - PLOG(ERROR) << "Failed to listen on '" << fd << "'"; - return; - } - - listener_fde = fdevent_create(fd, adbd_auth_listener, nullptr); - fdevent_add(listener_fde, FDE_READ); + AdbdAuthCallbacks cb; + cb.version = 1; + cb.callbacks.v1.key_authorized = adbd_auth_key_authorized; + auth_ctx = adbd_auth_new(&cb); + std::thread([]() { + adb_thread_setname("adbd auth"); + adbd_auth_run(auth_ctx); + LOG(FATAL) << "auth thread terminated"; + }).detach(); } void send_auth_request(atransport* t) { @@ -280,3 +149,18 @@ void adbd_auth_verified(atransport* t) { handle_online(t); send_connect(t); } + +static void adb_disconnected(void* unused, atransport* t) { + LOG(INFO) << "ADB disconnect"; + adbd_auth_notify_disconnect(auth_ctx, t->auth_id); +} + +void adbd_auth_confirm_key(atransport* t) { + LOG(INFO) << "prompting user to authorize key"; + t->AddDisconnect(&adb_disconnect); + adbd_auth_prompt_user(auth_ctx, t->auth_key.data(), t->auth_key.size(), t); +} + +void adbd_notify_framework_connected_key(atransport* t) { + adbd_auth_notify_auth(auth_ctx, t->auth_key.data(), t->auth_key.size()); +} diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp index 9ebab740e..70e4dd41d 100644 --- a/adb/daemon/main.cpp +++ b/adb/daemon/main.cpp @@ -214,8 +214,6 @@ int adbd_main(int server_port) { } #endif - adbd_auth_init(); - // Our external storage path may be different than apps, since // we aren't able to bind mount after dropping root. const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE"); @@ -230,6 +228,9 @@ int adbd_main(int server_port) { drop_privileges(server_port); #endif + // adbd_auth_init will spawn a thread, so we need to defer it until after selinux transitions. + adbd_auth_init(); + bool is_usb = false; #if defined(__ANDROID__) diff --git a/adb/transport.h b/adb/transport.h index c38cb1d8b..89d76b8c9 100644 --- a/adb/transport.h +++ b/adb/transport.h @@ -277,8 +277,11 @@ class atransport { std::string device; std::string devpath; +#if !ADB_HOST // Used to provide the key to the framework. std::string auth_key; + uint64_t auth_id; +#endif bool IsTcpDevice() const { return type == kTransportLocal; }