adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2015-09-23 00:52:57 +02:00
|
|
|
#define TRACE_TAG AUTH
|
2015-03-19 23:21:08 +01:00
|
|
|
|
2016-06-30 02:42:01 +02:00
|
|
|
#include "sysdeps.h"
|
2015-03-19 23:21:08 +01:00
|
|
|
|
2015-02-25 00:51:19 +01:00
|
|
|
#include <resolv.h>
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2019-04-26 03:33:35 +02:00
|
|
|
#include <algorithm>
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
#include <chrono>
|
2019-12-10 00:44:57 +01:00
|
|
|
#include <iomanip>
|
|
|
|
#include <map>
|
2016-06-30 02:42:01 +02:00
|
|
|
#include <memory>
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
#include <thread>
|
2016-06-30 02:42:01 +02:00
|
|
|
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
#include <adb/crypto/rsa_2048_key.h>
|
|
|
|
#include <adb/tls/adb_ca_list.h>
|
2019-10-22 21:30:39 +02:00
|
|
|
#include <adbd_auth.h>
|
2016-06-30 02:42:01 +02:00
|
|
|
#include <android-base/file.h>
|
2019-12-10 00:44:57 +01:00
|
|
|
#include <android-base/no_destructor.h>
|
2016-06-30 02:42:01 +02:00
|
|
|
#include <android-base/strings.h>
|
|
|
|
#include <crypto_utils/android_pubkey.h>
|
2016-03-31 16:32:09 +02:00
|
|
|
#include <openssl/obj_mac.h>
|
|
|
|
#include <openssl/rsa.h>
|
|
|
|
#include <openssl/sha.h>
|
2019-11-20 23:18:43 +01:00
|
|
|
#include <openssl/ssl.h>
|
2016-03-31 16:32:09 +02:00
|
|
|
|
2019-12-10 00:44:57 +01:00
|
|
|
#include "adb.h"
|
|
|
|
#include "adb_auth.h"
|
|
|
|
#include "adb_io.h"
|
2019-11-20 23:18:43 +01:00
|
|
|
#include "adb_wifi.h"
|
2019-12-10 00:44:57 +01:00
|
|
|
#include "fdevent/fdevent.h"
|
|
|
|
#include "transport.h"
|
|
|
|
#include "types.h"
|
|
|
|
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
using namespace adb::crypto;
|
|
|
|
using namespace adb::tls;
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
static AdbdAuthContext* auth_ctx;
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
static RSA* rsa_pkey = nullptr;
|
|
|
|
|
2018-12-29 05:35:37 +01:00
|
|
|
static void adb_disconnected(void* unused, atransport* t);
|
|
|
|
static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
|
2019-12-10 00:44:57 +01:00
|
|
|
static android::base::NoDestructor<std::map<uint32_t, weak_ptr<atransport>>> transports;
|
|
|
|
static uint32_t transport_auth_id = 0;
|
|
|
|
|
2016-10-06 04:02:29 +02:00
|
|
|
bool auth_required = true;
|
|
|
|
|
2019-12-10 00:44:57 +01:00
|
|
|
static void* transport_to_callback_arg(atransport* transport) {
|
|
|
|
uint32_t id = transport_auth_id++;
|
|
|
|
(*transports)[id] = transport->weak();
|
|
|
|
return reinterpret_cast<void*>(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static atransport* transport_from_callback_arg(void* id) {
|
|
|
|
uint64_t id_u64 = reinterpret_cast<uint64_t>(id);
|
|
|
|
if (id_u64 > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
LOG(FATAL) << "transport_from_callback_arg called on out of range value: " << id_u64;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t id_u32 = static_cast<uint32_t>(id_u64);
|
|
|
|
auto it = transports->find(id_u32);
|
|
|
|
if (it == transports->end()) {
|
|
|
|
LOG(ERROR) << "transport_from_callback_arg failed to find transport for id " << id_u32;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
atransport* t = it->second.get();
|
|
|
|
if (!t) {
|
|
|
|
LOG(WARNING) << "transport_from_callback_arg found already destructed transport";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
transports->erase(it);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
static void IteratePublicKeys(std::function<bool(std::string_view public_key)> f) {
|
|
|
|
adbd_auth_get_public_keys(
|
|
|
|
auth_ctx,
|
2020-02-19 03:29:25 +01:00
|
|
|
[](void* opaque, const char* public_key, size_t len) {
|
|
|
|
return (*static_cast<decltype(f)*>(opaque))(std::string_view(public_key, len));
|
2019-10-22 21:30:39 +02:00
|
|
|
},
|
|
|
|
&f);
|
|
|
|
}
|
|
|
|
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> adbd_tls_client_ca_list() {
|
|
|
|
if (!auth_required) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> ca_list(sk_X509_NAME_new_null());
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
bssl::UniquePtr<RSA> rsa_key(key);
|
|
|
|
|
|
|
|
unsigned char* dkey = nullptr;
|
|
|
|
int len = i2d_RSA_PUBKEY(rsa_key.get(), &dkey);
|
|
|
|
if (len <= 0 || dkey == nullptr) {
|
|
|
|
LOG(ERROR) << "Failed to encode RSA public key";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t digest[SHA256_DIGEST_LENGTH];
|
|
|
|
// Put the encoded key in the commonName attribute of the issuer name.
|
|
|
|
// Note that the commonName has a max length of 64 bytes, which is less
|
|
|
|
// than the SHA256_DIGEST_LENGTH.
|
|
|
|
SHA256(dkey, len, digest);
|
|
|
|
OPENSSL_free(dkey);
|
|
|
|
|
|
|
|
auto digest_str = SHA256BitsToHexString(
|
|
|
|
std::string_view(reinterpret_cast<const char*>(&digest[0]), sizeof(digest)));
|
|
|
|
LOG(INFO) << "fingerprint=[" << digest_str << "]";
|
|
|
|
auto issuer = CreateCAIssuerFromEncodedKey(digest_str);
|
|
|
|
CHECK(bssl::PushToStack(ca_list.get(), std::move(issuer)));
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
return ca_list;
|
|
|
|
}
|
|
|
|
|
2019-04-26 03:33:35 +02:00
|
|
|
bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
|
|
|
|
std::string* auth_key) {
|
2019-10-22 21:30:39 +02:00
|
|
|
bool authorized = false;
|
|
|
|
auth_key->clear();
|
2016-06-30 02:42:01 +02:00
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
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;
|
|
|
|
}
|
2016-06-30 02:42:01 +02:00
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
RSA* key = nullptr;
|
|
|
|
if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
|
|
|
|
LOG(ERROR) << "Failed to parse key " << pubkey;
|
|
|
|
return true;
|
|
|
|
}
|
2016-06-30 02:42:01 +02:00
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
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;
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
}
|
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
return true;
|
|
|
|
});
|
2019-04-26 03:33:35 +02:00
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
return authorized;
|
2019-04-26 03:33:35 +02:00
|
|
|
}
|
|
|
|
|
2016-10-06 04:02:29 +02:00
|
|
|
static bool adbd_auth_generate_token(void* token, size_t token_size) {
|
2016-06-30 02:42:01 +02:00
|
|
|
FILE* fp = fopen("/dev/urandom", "re");
|
|
|
|
if (!fp) return false;
|
|
|
|
bool okay = (fread(token, token_size, 1, fp) == 1);
|
|
|
|
fclose(fp);
|
|
|
|
return okay;
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
}
|
|
|
|
|
2015-03-17 19:03:36 +01:00
|
|
|
void adbd_cloexec_auth_socket() {
|
|
|
|
int fd = android_get_control_socket("adbd");
|
|
|
|
if (fd == -1) {
|
2016-06-30 02:42:01 +02:00
|
|
|
PLOG(ERROR) << "Failed to get adbd socket";
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-07-19 05:57:35 +02:00
|
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
2015-03-17 19:03:36 +01:00
|
|
|
}
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
static void adbd_auth_key_authorized(void* arg, uint64_t id) {
|
|
|
|
LOG(INFO) << "adb client authorized";
|
2019-12-10 00:44:57 +01:00
|
|
|
fdevent_run_on_main_thread([=]() {
|
|
|
|
LOG(INFO) << "arg = " << reinterpret_cast<uintptr_t>(arg);
|
|
|
|
auto* transport = transport_from_callback_arg(arg);
|
|
|
|
if (!transport) {
|
|
|
|
LOG(ERROR) << "authorization received for deleted transport, ignoring";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
transport->auth_id = id;
|
|
|
|
adbd_auth_verified(transport);
|
|
|
|
});
|
2019-10-22 21:30:39 +02:00
|
|
|
}
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
|
2019-11-20 23:18:43 +01:00
|
|
|
static void adbd_key_removed(const char* public_key, size_t len) {
|
|
|
|
// The framework removed the key from its keystore. We need to disconnect all
|
|
|
|
// devices using that key. Search by t->auth_key
|
|
|
|
std::string_view auth_key(public_key, len);
|
|
|
|
kick_all_transports_by_auth_key(auth_key);
|
|
|
|
}
|
|
|
|
|
2019-10-22 21:30:39 +02:00
|
|
|
void adbd_auth_init(void) {
|
2020-02-19 03:29:25 +01:00
|
|
|
AdbdAuthCallbacksV1 cb;
|
2019-10-22 21:30:39 +02:00
|
|
|
cb.version = 1;
|
2020-02-19 03:29:25 +01:00
|
|
|
cb.key_authorized = adbd_auth_key_authorized;
|
2019-11-20 23:18:43 +01:00
|
|
|
cb.key_removed = adbd_key_removed;
|
2019-10-22 21:30:39 +02:00
|
|
|
auth_ctx = adbd_auth_new(&cb);
|
2019-11-20 23:18:43 +01:00
|
|
|
adbd_wifi_init(auth_ctx);
|
2019-10-22 21:30:39 +02:00
|
|
|
std::thread([]() {
|
|
|
|
adb_thread_setname("adbd auth");
|
|
|
|
adbd_auth_run(auth_ctx);
|
|
|
|
LOG(FATAL) << "auth thread terminated";
|
|
|
|
}).detach();
|
adb: Add public key authentification
Secure adb using a public key authentication, to allow USB debugging
only from authorized hosts.
When a device is connected to an unauthorized host, the adb daemon sends
the user public key to the device. A popup is shown to ask the user to
allow debugging once or permanantly from the host. The public key is
installed on the device in the later case. Other keys may be installed
at build time.
On the host, the user public/private key pair is automatically generated,
if it does not exist, when the adb daemon starts and is stored in
$HOME/.android/adb_key(.pub) or in $ANDROID_SDK_HOME on windows. If needed,
the ADB_KEYS_PATH env variable may be set to a :-separated (; under
Windows) list of private keys, e.g. company-wide or vendor keys.
On the device, vendors public keys are installed at build time in
/adb_keys. User-installed keys are stored in /data/misc/adb/adb_keys.
ADB Protocol change:
If the device needs to authenticate the host, it replies to CNXN
packets with an AUTH packet. The AUTH packet payload is a random token.
The host signs the token with one of its private keys and sends an AUTH(0)
packet. If the signature verification succeeds, the device replies with
a CNXN packet. Otherwise, it sends a new AUTH packet with a new token so
that the host can retry with another private key. Once the host has tried
all its keys, it can send an AUTH(1) packet with a public key as
payload. adbd then sends the public key to the framework (if it has been
started) for confirmation.
Change-Id: I4e84d7621da956f66ff657245901bdaefead8395
2012-04-12 21:23:49 +02:00
|
|
|
}
|
2016-10-06 04:02:29 +02:00
|
|
|
|
|
|
|
void send_auth_request(atransport* t) {
|
|
|
|
LOG(INFO) << "Calling send_auth_request...";
|
|
|
|
|
|
|
|
if (!adbd_auth_generate_token(t->token, sizeof(t->token))) {
|
|
|
|
PLOG(ERROR) << "Error generating token";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
apacket* p = get_apacket();
|
|
|
|
p->msg.command = A_AUTH;
|
|
|
|
p->msg.arg0 = ADB_AUTH_TOKEN;
|
|
|
|
p->msg.data_length = sizeof(t->token);
|
2018-02-06 03:49:10 +01:00
|
|
|
p->payload.assign(t->token, t->token + sizeof(t->token));
|
2016-10-06 04:02:29 +02:00
|
|
|
send_packet(p, t);
|
|
|
|
}
|
|
|
|
|
2017-07-26 20:06:55 +02:00
|
|
|
void adbd_auth_verified(atransport* t) {
|
|
|
|
LOG(INFO) << "adb client authorized";
|
2016-10-06 04:02:29 +02:00
|
|
|
handle_online(t);
|
|
|
|
send_connect(t);
|
|
|
|
}
|
2019-10-22 21:30:39 +02:00
|
|
|
|
|
|
|
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);
|
2019-12-10 00:44:57 +01:00
|
|
|
adbd_auth_prompt_user(auth_ctx, t->auth_key.data(), t->auth_key.size(),
|
|
|
|
transport_to_callback_arg(t));
|
2019-10-22 21:30:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void adbd_notify_framework_connected_key(atransport* t) {
|
[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
2020-01-21 22:19:42 +01:00
|
|
|
t->auth_id = adbd_auth_notify_auth(auth_ctx, t->auth_key.data(), t->auth_key.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
int adbd_tls_verify_cert(X509_STORE_CTX* ctx, std::string* auth_key) {
|
|
|
|
if (!auth_required) {
|
|
|
|
// Any key will do.
|
|
|
|
LOG(INFO) << __func__ << ": auth not required";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool authorized = false;
|
|
|
|
X509* cert = X509_STORE_CTX_get0_cert(ctx);
|
|
|
|
if (cert == nullptr) {
|
|
|
|
LOG(INFO) << "got null x509 certificate";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
bssl::UniquePtr<EVP_PKEY> evp_pkey(X509_get_pubkey(cert));
|
|
|
|
if (evp_pkey == nullptr) {
|
|
|
|
LOG(INFO) << "got null evp_pkey from x509 certificate";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = false;
|
|
|
|
bssl::UniquePtr<EVP_PKEY> known_evp(EVP_PKEY_new());
|
|
|
|
EVP_PKEY_set1_RSA(known_evp.get(), key);
|
|
|
|
if (EVP_PKEY_cmp(known_evp.get(), evp_pkey.get())) {
|
|
|
|
LOG(INFO) << "Matched auth_key=" << public_key;
|
|
|
|
verified = true;
|
|
|
|
} else {
|
|
|
|
LOG(INFO) << "auth_key doesn't match [" << public_key << "]";
|
|
|
|
}
|
|
|
|
RSA_free(key);
|
|
|
|
if (verified) {
|
|
|
|
*auth_key = public_key;
|
|
|
|
authorized = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
return authorized ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void adbd_auth_tls_handshake(atransport* t) {
|
|
|
|
if (rsa_pkey == nullptr) {
|
|
|
|
// Generate a random RSA key to feed into the X509 certificate
|
|
|
|
auto rsa_2048 = CreateRSA2048Key();
|
|
|
|
CHECK(rsa_2048.has_value());
|
|
|
|
rsa_pkey = EVP_PKEY_get1_RSA(rsa_2048->GetEvpPkey());
|
|
|
|
CHECK(rsa_pkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::thread([t]() {
|
|
|
|
std::string auth_key;
|
|
|
|
if (t->connection()->DoTlsHandshake(rsa_pkey, &auth_key)) {
|
|
|
|
LOG(INFO) << "auth_key=" << auth_key;
|
|
|
|
if (t->IsTcpDevice()) {
|
|
|
|
t->auth_key = auth_key;
|
|
|
|
adbd_wifi_secure_connect(t);
|
|
|
|
} else {
|
|
|
|
adbd_auth_verified(t);
|
|
|
|
adbd_notify_framework_connected_key(t);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Only allow one attempt at the handshake.
|
|
|
|
t->Kick();
|
|
|
|
}
|
|
|
|
}).detach();
|
2019-10-22 21:30:39 +02:00
|
|
|
}
|