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
This commit is contained in:
parent
50e102c51d
commit
275232667d
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <adbd_auth.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <crypto_utils/android_pubkey.h>
|
||||
|
@ -38,85 +39,57 @@
|
|||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
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<std::string>;
|
||||
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<char*>(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<const uint8_t*>(token), token_size,
|
||||
reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(),
|
||||
key) == 1);
|
||||
RSA_free(key);
|
||||
if (verified) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
auth_key->clear();
|
||||
return false;
|
||||
static void IteratePublicKeys(std::function<bool(std::string_view public_key)> f) {
|
||||
adbd_auth_get_public_keys(
|
||||
auth_ctx,
|
||||
[](const char* public_key, size_t len, void* arg) {
|
||||
return (*static_cast<decltype(f)*>(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<int>(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<std::string> 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<const uint8_t*>(token), token_size,
|
||||
reinterpret_cast<const uint8_t*>(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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<atransport*>(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());
|
||||
}
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue