Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5cf7868b authored by Joshua Duong's avatar Joshua Duong
Browse files

[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
parent d85f5c01
Loading
Loading
Loading
Loading
+32 −1
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@
#include "adb_listeners.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
#include "adb_wifi.h"
#include "sysdeps/chrono.h"
#include "transport.h"

@@ -140,6 +141,9 @@ void print_packet(const char *label, apacket *p)
    case A_CLSE: tag = "CLSE"; break;
    case A_WRTE: tag = "WRTE"; break;
    case A_AUTH: tag = "AUTH"; break;
    case A_STLS:
        tag = "ATLS";
        break;
    default: tag = "????"; break;
    }

@@ -209,6 +213,15 @@ std::string get_connection_string() {
        android::base::Join(connection_properties, ';').c_str());
}

void send_tls_request(atransport* t) {
    D("Calling send_tls_request");
    apacket* p = get_apacket();
    p->msg.command = A_STLS;
    p->msg.arg0 = A_STLS_VERSION;
    p->msg.data_length = 0;
    send_packet(p, t);
}

void send_connect(atransport* t) {
    D("Calling send_connect");
    apacket* cp = get_apacket();
@@ -299,7 +312,12 @@ static void handle_new_connection(atransport* t, apacket* p) {
#if ADB_HOST
    handle_online(t);
#else
    if (!auth_required) {
    if (t->use_tls) {
        // We still handshake in TLS mode. If auth_required is disabled,
        // we'll just not verify the client's certificate. This should be the
        // first packet the client receives to indicate the new protocol.
        send_tls_request(t);
    } else if (!auth_required) {
        LOG(INFO) << "authentication not required";
        handle_online(t);
        send_connect(t);
@@ -324,8 +342,21 @@ void handle_packet(apacket *p, atransport *t)
    case A_CNXN:  // CONNECT(version, maxdata, "system-id-string")
        handle_new_connection(t, p);
        break;
    case A_STLS:  // TLS(version, "")
        t->use_tls = true;
#if ADB_HOST
        send_tls_request(t);
        adb_auth_tls_handshake(t);
#else
        adbd_auth_tls_handshake(t);
#endif
        break;

    case A_AUTH:
        // All AUTH commands are ignored in TLS mode
        if (t->use_tls) {
            break;
        }
        switch (p->msg.arg0) {
#if ADB_HOST
            case ADB_AUTH_TOKEN:
+6 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
#define A_CLSE 0x45534c43
#define A_WRTE 0x45545257
#define A_AUTH 0x48545541
#define A_STLS 0x534C5453

// ADB protocol version.
// Version revision:
@@ -53,6 +54,10 @@ constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
#define A_VERSION_SKIP_CHECKSUM 0x01000001
#define A_VERSION 0x01000001

// Stream-based TLS protocol version
#define A_STLS_VERSION_MIN 0x01000000
#define A_STLS_VERSION 0x01000000

// Used for help/version information.
#define ADB_VERSION_MAJOR 1
#define ADB_VERSION_MINOR 0
@@ -229,6 +234,7 @@ void handle_online(atransport* t);
void handle_offline(atransport* t);

void send_connect(atransport* t);
void send_tls_request(atransport* t);

void parse_banner(const std::string&, atransport* t);

+7 −0
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@ std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();

void send_auth_response(const char* token, size_t token_size, atransport* t);

int adb_tls_set_certificate(SSL* ssl);
void adb_auth_tls_handshake(atransport* t);

#else // !ADB_HOST

extern bool auth_required;
@@ -58,6 +61,10 @@ void adbd_notify_framework_connected_key(atransport* t);

void send_auth_request(atransport *t);

void adbd_auth_tls_handshake(atransport* t);
int adbd_tls_verify_cert(X509_STORE_CTX* ctx, std::string* auth_key);
bssl::UniquePtr<STACK_OF(X509_NAME)> adbd_tls_client_ca_list();

#endif // ADB_HOST

#endif // __ADB_AUTH_H
+74 −0
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@
#include <string>

#include <adb/crypto/rsa_2048_key.h>
#include <adb/crypto/x509_generator.h>
#include <adb/tls/adb_ca_list.h>
#include <adb/tls/tls_connection.h>
#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -55,6 +58,7 @@ static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;

using namespace adb::crypto;
using namespace adb::tls;

static bool generate_key(const std::string& file) {
    LOG(INFO) << "generate_key(" << file << ")...";
@@ -144,6 +148,7 @@ static bool load_key(const std::string& file) {
    if (g_keys.find(fingerprint) != g_keys.end()) {
        LOG(INFO) << "ignoring already-loaded key: " << file;
    } else {
        LOG(INFO) << "Loaded fingerprint=[" << SHA256BitsToHexString(fingerprint) << "]";
        g_keys[fingerprint] = std::move(key);
    }
    return true;
@@ -475,3 +480,72 @@ void send_auth_response(const char* token, size_t token_size, atransport* t) {
    p->msg.data_length = p->payload.size();
    send_packet(p, t);
}

void adb_auth_tls_handshake(atransport* t) {
    std::thread([t]() {
        std::shared_ptr<RSA> key = t->Key();
        if (key == nullptr) {
            // Can happen if !auth_required
            LOG(INFO) << "t->auth_key not set before handshake";
            key = t->NextKey();
            CHECK(key);
        }

        LOG(INFO) << "Attempting to TLS handshake";
        bool success = t->connection()->DoTlsHandshake(key.get());
        if (success) {
            LOG(INFO) << "Handshake succeeded. Waiting for CNXN packet...";
        } else {
            LOG(INFO) << "Handshake failed. Kicking transport";
            t->Kick();
        }
    }).detach();
}

int adb_tls_set_certificate(SSL* ssl) {
    LOG(INFO) << __func__;

    const STACK_OF(X509_NAME)* ca_list = SSL_get_client_CA_list(ssl);
    if (ca_list == nullptr) {
        // Either the device doesn't know any keys, or !auth_required.
        // So let's just try with the default certificate and see what happens.
        LOG(INFO) << "No client CA list. Trying with default certificate.";
        return 1;
    }

    const size_t num_cas = sk_X509_NAME_num(ca_list);
    for (size_t i = 0; i < num_cas; ++i) {
        auto* x509_name = sk_X509_NAME_value(ca_list, i);
        auto adbFingerprint = ParseEncodedKeyFromCAIssuer(x509_name);
        if (!adbFingerprint.has_value()) {
            // This could be a real CA issuer. Unfortunately, we don't support
            // it ATM.
            continue;
        }

        LOG(INFO) << "Checking for fingerprint match [" << *adbFingerprint << "]";
        auto encoded_key = SHA256HexStringToBits(*adbFingerprint);
        if (!encoded_key.has_value()) {
            continue;
        }
        // Check against our list of encoded keys for a match
        std::lock_guard<std::mutex> lock(g_keys_mutex);
        auto rsa_priv_key = g_keys.find(*encoded_key);
        if (rsa_priv_key != g_keys.end()) {
            LOG(INFO) << "Got SHA256 match on a key";
            bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
            CHECK(EVP_PKEY_set1_RSA(evp_pkey.get(), rsa_priv_key->second.get()));
            auto x509 = GenerateX509Certificate(evp_pkey.get());
            auto x509_str = X509ToPEMString(x509.get());
            auto evp_str = Key::ToPEMString(evp_pkey.get());
            TlsConnection::SetCertAndKey(ssl, x509_str, evp_str);
            return 1;
        } else {
            LOG(INFO) << "No match for [" << *adbFingerprint << "]";
        }
    }

    // Let's just try with the default certificate anyways, because daemon might
    // not require auth, even though it has a list of keys.
    return 1;
}
+4 −3
Original line number Diff line number Diff line
@@ -142,9 +142,9 @@ void TlsServer::OnFdEvent(int fd, unsigned ev) {
        close_on_exec(new_fd.get());
        disable_tcp_nagle(new_fd.get());
        std::string serial = android::base::StringPrintf("host-%d", new_fd.get());
        // TODO: register a tls transport
        //        register_socket_transport(std::move(new_fd), std::move(serial), port_, 1,
        //                                  [](atransport*) { return ReconnectResult::Abort; });
        register_socket_transport(
                std::move(new_fd), std::move(serial), port_, 1,
                [](atransport*) { return ReconnectResult::Abort; }, true);
    }
}

@@ -224,4 +224,5 @@ void adbd_wifi_secure_connect(atransport* t) {
    t->auth_id = adbd_auth_tls_device_connected(auth_ctx, kAdbTransportTypeWifi, t->auth_key.data(),
                                                t->auth_key.size());
}

#endif /* !HOST */
Loading