Merge "Adding new GSI public keys"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d6945e3..0ec505d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,6 +4,12 @@
"name": "adbd_test"
},
{
+ "name": "adb_crypto_test"
+ },
+ {
+ "name": "adb_tls_connection_test"
+ },
+ {
"name": "CtsInitTestCases"
},
{
diff --git a/adb/Android.bp b/adb/Android.bp
index 675525c..32581a2 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -255,6 +255,8 @@
},
static_libs: [
+ "libadb_crypto",
+ "libadb_protos",
"libbase",
"libcrypto_utils",
"libcrypto",
@@ -272,6 +274,7 @@
defaults: ["adb_defaults"],
srcs: libadb_test_srcs,
static_libs: [
+ "libadb_crypto",
"libadb_host",
"libbase",
"libcutils",
@@ -338,6 +341,8 @@
"client/line_printer.cpp",
"client/fastdeploy.cpp",
"client/fastdeploycallbacks.cpp",
+ "client/incremental.cpp",
+ "client/incremental_server.cpp",
"shell_service_protocol.cpp",
],
@@ -347,6 +352,7 @@
],
static_libs: [
+ "libadb_crypto",
"libadb_host",
"libandroidfw",
"libbase",
@@ -356,6 +362,7 @@
"libfastdeploy_host",
"libdiagnose_usb",
"liblog",
+ "liblz4",
"libmdnssd",
"libprotobuf-cpp-lite",
"libusb",
@@ -422,6 +429,7 @@
],
shared_libs: [
+ "libadb_crypto",
"libadbd_auth",
"libasyncio",
"libbase",
@@ -765,6 +773,7 @@
"fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
],
static_libs: [
+ "libadb_crypto",
"libadb_host",
"libandroidfw",
"libbase",
diff --git a/adb/adb_mdns.h b/adb/adb_mdns.h
index 2e544d7..6b37355 100644
--- a/adb/adb_mdns.h
+++ b/adb/adb_mdns.h
@@ -17,6 +17,72 @@
#ifndef _ADB_MDNS_H_
#define _ADB_MDNS_H_
+#include <android-base/macros.h>
+
const char* kADBServiceType = "_adb._tcp";
+const char* kADBSecurePairingServiceType = "_adb_secure_pairing._tcp";
+const char* kADBSecureConnectServiceType = "_adb_secure_connect._tcp";
+
+const int kADBTransportServiceRefIndex = 0;
+const int kADBSecurePairingServiceRefIndex = 1;
+const int kADBSecureConnectServiceRefIndex = 2;
+
+// Each ADB Secure service advertises with a TXT record indicating the version
+// using a key/value pair per RFC 6763 (https://tools.ietf.org/html/rfc6763).
+//
+// The first key/value pair is always the version of the protocol.
+// There may be more key/value pairs added after.
+//
+// The version is purposely represented as the single letter "v" due to the
+// need to minimize DNS traffic. The version starts at 1. With each breaking
+// protocol change, the version is incremented by 1.
+//
+// Newer adb clients/daemons need to recognize and either reject
+// or be backward-compatible with older verseions if there is a mismatch.
+//
+// Relevant sections:
+//
+// """
+// 6.4. Rules for Keys in DNS-SD Key/Value Pairs
+//
+// The key MUST be at least one character. DNS-SD TXT record strings
+// beginning with an '=' character (i.e., the key is missing) MUST be
+// silently ignored.
+//
+// ...
+//
+// 6.5. Rules for Values in DNS-SD Key/Value Pairs
+//
+// If there is an '=' in a DNS-SD TXT record string, then everything
+// after the first '=' to the end of the string is the value. The value
+// can contain any eight-bit values including '='.
+// """
+
+#define ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ver) ("v=" #ver)
+
+// Client/service versions are initially defined to be matching,
+// but may go out of sync as different clients and services
+// try to talk to each other.
+#define ADB_SECURE_SERVICE_VERSION 1
+#define ADB_SECURE_CLIENT_VERSION ADB_SECURE_SERVICE_VERSION
+
+const char* kADBSecurePairingServiceTxtRecord =
+ ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
+const char* kADBSecureConnectServiceTxtRecord =
+ ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
+
+const char* kADBDNSServices[] = {
+ kADBServiceType,
+ kADBSecurePairingServiceType,
+ kADBSecureConnectServiceType,
+};
+
+const char* kADBDNSServiceTxtRecords[] = {
+ nullptr,
+ kADBSecurePairingServiceTxtRecord,
+ kADBSecureConnectServiceTxtRecord,
+};
+
+const int kNumADBDNSServices = arraysize(kADBDNSServices);
#endif
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 80f146c..cea24fe 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -118,22 +118,22 @@
return;
}
- std::unordered_map<std::string, int> trace_flags = {
- {"1", -1},
- {"all", -1},
- {"adb", ADB},
- {"sockets", SOCKETS},
- {"packets", PACKETS},
- {"rwx", RWX},
- {"usb", USB},
- {"sync", SYNC},
- {"sysdeps", SYSDEPS},
- {"transport", TRANSPORT},
- {"jdwp", JDWP},
- {"services", SERVICES},
- {"auth", AUTH},
- {"fdevent", FDEVENT},
- {"shell", SHELL}};
+ std::unordered_map<std::string, int> trace_flags = {{"1", -1},
+ {"all", -1},
+ {"adb", ADB},
+ {"sockets", SOCKETS},
+ {"packets", PACKETS},
+ {"rwx", RWX},
+ {"usb", USB},
+ {"sync", SYNC},
+ {"sysdeps", SYSDEPS},
+ {"transport", TRANSPORT},
+ {"jdwp", JDWP},
+ {"services", SERVICES},
+ {"auth", AUTH},
+ {"fdevent", FDEVENT},
+ {"shell", SHELL},
+ {"incremental", INCREMENTAL}};
std::vector<std::string> elements = android::base::Split(trace_setting, " ");
for (const auto& elem : elements) {
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 1d2c8c7..ed4be88 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -25,19 +25,20 @@
* the adb_trace_init() function implemented in adb_trace.cpp.
*/
enum AdbTrace {
- ADB = 0, /* 0x001 */
+ ADB = 0, /* 0x001 */
SOCKETS,
PACKETS,
TRANSPORT,
- RWX, /* 0x010 */
+ RWX, /* 0x010 */
USB,
SYNC,
SYSDEPS,
- JDWP, /* 0x100 */
+ JDWP, /* 0x100 */
SERVICES,
AUTH,
FDEVENT,
- SHELL
+ SHELL,
+ INCREMENTAL,
};
#define VLOG_IS_ON(TAG) \
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index ba53041..758fcab 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <optional>
#include <string>
@@ -86,3 +87,16 @@
// Globally acccesible argv/envp, for the purpose of re-execing adb.
extern const char* _Nullable * _Nullable __adb_argv;
extern const char* _Nullable * _Nullable __adb_envp;
+
+// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
+// resolved, and to run some kind of callback for each one.
+using adb_secure_foreach_service_callback = std::function<void(
+ const char* _Nonnull host_name, const char* _Nonnull ip_address, uint16_t port)>;
+
+// Queries pairing/connect services that have been discovered and resolved.
+// If |host_name| is not null, run |cb| only for services
+// matching |host_name|. Otherwise, run for all services.
+void adb_secure_foreach_pairing_service(const char* _Nullable host_name,
+ adb_secure_foreach_service_callback cb);
+void adb_secure_foreach_connect_service(const char* _Nullable host_name,
+ adb_secure_foreach_service_callback cb);
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 2bcd0a6..982a96b 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -36,8 +36,10 @@
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
+#include "incremental.h"
static constexpr int kFastDeployMinApi = 24;
+static constexpr int kIncrementalMinApi = 29;
namespace {
@@ -45,8 +47,8 @@
INSTALL_DEFAULT,
INSTALL_PUSH,
INSTALL_STREAM,
+ INSTALL_INCREMENTAL,
};
-
}
static bool can_use_feature(const char* feature) {
@@ -70,6 +72,10 @@
return can_use_feature(kFeatureApex);
}
+static bool is_abb_exec_supported() {
+ return can_use_feature(kFeatureAbbExec);
+}
+
static int pm_command(int argc, const char** argv) {
std::string cmd = "pm";
@@ -193,14 +199,14 @@
posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
#endif
- const bool use_abb = can_use_feature(kFeatureAbbExec);
+ const bool use_abb_exec = is_abb_exec_supported();
std::string error;
- std::vector<std::string> cmd_args = {use_abb ? "package" : "exec:cmd package"};
+ std::vector<std::string> cmd_args = {use_abb_exec ? "package" : "exec:cmd package"};
cmd_args.reserve(argc + 3);
// don't copy the APK name, but, copy the rest of the arguments as-is
while (argc-- > 1) {
- if (use_abb) {
+ if (use_abb_exec) {
cmd_args.push_back(*argv++);
} else {
cmd_args.push_back(escape_arg(*argv++));
@@ -217,7 +223,7 @@
}
unique_fd remote_fd;
- if (use_abb) {
+ if (use_abb_exec) {
remote_fd = send_abb_exec_command(cmd_args, &error);
} else {
remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
@@ -287,8 +293,60 @@
return result;
}
+template <class TimePoint>
+static int msBetween(TimePoint start, TimePoint end) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
+
+static int install_app_incremental(int argc, const char** argv) {
+ printf("Performing Incremental Install\n");
+ using clock = std::chrono::high_resolution_clock;
+ const auto start = clock::now();
+ int first_apk = -1;
+ int last_apk = -1;
+ std::string cert_path;
+ bool wait = false;
+ std::vector<std::string_view> args = {"package"};
+ for (int i = 0; i < argc; ++i) {
+ const auto arg = std::string_view(argv[i]);
+ if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
+ last_apk = i;
+ if (first_apk == -1) {
+ first_apk = i;
+ }
+ } else if (arg == "--wait") {
+ wait = true;
+ } else if (arg.starts_with("install-")) {
+ // incremental installation command on the device is the same for all its variations in
+ // the adb, e.g. install-multiple or install-multi-package
+ args.push_back("install");
+ } else {
+ args.push_back(arg);
+ }
+ }
+
+ if (first_apk == -1) error_exit("Need at least one APK file on command line");
+
+ const auto afterApk = clock::now();
+
+ auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
+ if (!server_process) {
+ return -1;
+ }
+
+ const auto end = clock::now();
+ printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
+ msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
+
+ if (wait) {
+ (*server_process).wait();
+ }
+
+ return 0;
+}
+
int install_app(int argc, const char** argv) {
- std::vector<int> processedArgIndicies;
+ std::vector<int> processedArgIndices;
InstallMode installMode = INSTALL_DEFAULT;
bool use_fastdeploy = false;
bool is_reinstall = false;
@@ -296,30 +354,42 @@
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--streaming")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
installMode = INSTALL_STREAM;
} else if (!strcmp(argv[i], "--no-streaming")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
installMode = INSTALL_PUSH;
} else if (!strcmp(argv[i], "-r")) {
- // Note that this argument is not added to processedArgIndicies because it
+ // Note that this argument is not added to processedArgIndices because it
// must be passed through to pm
is_reinstall = true;
} else if (!strcmp(argv[i], "--fastdeploy")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
use_fastdeploy = true;
} else if (!strcmp(argv[i], "--no-fastdeploy")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
use_fastdeploy = false;
} else if (!strcmp(argv[i], "--force-agent")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateAlways;
} else if (!strcmp(argv[i], "--date-check-agent")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
} else if (!strcmp(argv[i], "--version-check-agent")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+ } else if (!strcmp(argv[i], "--incremental")) {
+ processedArgIndices.push_back(i);
+ installMode = INSTALL_INCREMENTAL;
+ } else if (!strcmp(argv[i], "--no-incremental")) {
+ processedArgIndices.push_back(i);
+ installMode = INSTALL_DEFAULT;
+ }
+ }
+
+ if (installMode == INSTALL_INCREMENTAL) {
+ if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
+ error_exit("Attempting to use incremental install on unsupported device");
}
}
@@ -341,8 +411,8 @@
std::vector<const char*> passthrough_argv;
for (int i = 0; i < argc; i++) {
- if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
- processedArgIndicies.end()) {
+ if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
+ processedArgIndices.end()) {
passthrough_argv.push_back(argv[i]);
}
}
@@ -357,6 +427,8 @@
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
+ case INSTALL_INCREMENTAL:
+ return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
case INSTALL_DEFAULT:
default:
return 1;
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index e8be784..dcf4bc0 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -29,6 +29,7 @@
#include <set>
#include <string>
+#include <adb/crypto/rsa_2048_key.h>
#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -53,100 +54,50 @@
*new std::map<std::string, std::shared_ptr<RSA>>;
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
-static std::string get_user_info() {
- std::string hostname;
- if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
- char buf[64];
- if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
- if (hostname.empty()) hostname = "unknown";
+using namespace adb::crypto;
- std::string username;
- if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined(_WIN32)
- if (username.empty() && getlogin()) username = getlogin();
-#endif
- if (username.empty()) hostname = "unknown";
-
- return " " + username + "@" + hostname;
-}
-
-static bool calculate_public_key(std::string* out, RSA* private_key) {
- uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
- if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
- LOG(ERROR) << "Failed to convert to public key";
- return false;
- }
-
- size_t expected_length;
- if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
- LOG(ERROR) << "Public key too large to base64 encode";
- return false;
- }
-
- out->resize(expected_length);
- size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
- sizeof(binary_key_data));
- out->resize(actual_length);
- out->append(get_user_info());
- return true;
-}
-
-static int generate_key(const std::string& file) {
+static bool generate_key(const std::string& file) {
LOG(INFO) << "generate_key(" << file << ")...";
- mode_t old_mask;
- FILE *f = nullptr;
- int ret = 0;
+ auto rsa_2048 = CreateRSA2048Key();
+ if (!rsa_2048) {
+ LOG(ERROR) << "Unable to create key";
+ return false;
+ }
std::string pubkey;
- EVP_PKEY* pkey = EVP_PKEY_new();
- BIGNUM* exponent = BN_new();
- RSA* rsa = RSA_new();
- if (!pkey || !exponent || !rsa) {
- LOG(ERROR) << "Failed to allocate key";
- goto out;
- }
+ RSA* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ CHECK(rsa);
- BN_set_word(exponent, RSA_F4);
- RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
- EVP_PKEY_set1_RSA(pkey, rsa);
-
- if (!calculate_public_key(&pubkey, rsa)) {
+ if (!CalculatePublicKey(&pubkey, rsa)) {
LOG(ERROR) << "failed to calculate public key";
- goto out;
+ return false;
}
- old_mask = umask(077);
+ mode_t old_mask = umask(077);
- f = fopen(file.c_str(), "w");
+ std::unique_ptr<FILE, decltype(&fclose)> f(nullptr, &fclose);
+ f.reset(fopen(file.c_str(), "w"));
if (!f) {
PLOG(ERROR) << "Failed to open " << file;
umask(old_mask);
- goto out;
+ return false;
}
umask(old_mask);
- if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
+ if (!PEM_write_PrivateKey(f.get(), rsa_2048->GetEvpPkey(), nullptr, nullptr, 0, nullptr,
+ nullptr)) {
LOG(ERROR) << "Failed to write key";
- goto out;
+ return false;
}
if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
PLOG(ERROR) << "failed to write public key";
- goto out;
+ return false;
}
- ret = 1;
-
-out:
- if (f) fclose(f);
- EVP_PKEY_free(pkey);
- RSA_free(rsa);
- BN_free(exponent);
- return ret;
+ return true;
}
static std::string hash_key(RSA* key) {
@@ -325,7 +276,7 @@
if (!privkey) {
return false;
}
- return calculate_public_key(out, privkey.get());
+ return CalculatePublicKey(out, privkey.get());
}
std::string adb_auth_get_userkey() {
@@ -343,7 +294,7 @@
}
int adb_auth_keygen(const char* filename) {
- return (generate_key(filename) == 0);
+ return !generate_key(filename);
}
int adb_auth_pubkey(const char* filename) {
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index c302965..84c0e01 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -60,6 +60,7 @@
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
+#include "incremental_server.h"
#include "services.h"
#include "shell_protocol.h"
#include "sysdeps/chrono.h"
@@ -1959,6 +1960,18 @@
error_exit("usage: adb reconnect [device|offline]");
}
}
+ } else if (!strcmp(argv[0], "inc-server")) {
+ if (argc < 3) {
+ error_exit("usage: adb inc-server FD FILE1 FILE2 ...");
+ }
+ int fd = atoi(argv[1]);
+ if (fd < 3) {
+ // Disallow invalid FDs and stdin/out/err as well.
+ error_exit("Invalid fd number given: %d", fd);
+ }
+ fd = adb_register_socket(fd);
+ close_on_exec(fd);
+ return incremental::serve(fd, argc - 2, argv + 2);
}
error_exit("unknown command %s", argv[0]);
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 5fa0edb..c5fc12f 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -81,17 +81,21 @@
} // namespace
int get_device_api_level() {
- REPORT_FUNC_TIME();
- std::vector<char> sdk_version_output_buffer;
- std::vector<char> sdk_version_error_buffer;
- int api_level = -1;
+ static const int api_level = [] {
+ REPORT_FUNC_TIME();
+ std::vector<char> sdk_version_output_buffer;
+ std::vector<char> sdk_version_error_buffer;
+ int api_level = -1;
- int statusCode = capture_shell_command("getprop ro.build.version.sdk",
- &sdk_version_output_buffer, &sdk_version_error_buffer);
- if (statusCode == 0 && sdk_version_output_buffer.size() > 0) {
- api_level = strtol((char*)sdk_version_output_buffer.data(), NULL, 10);
- }
+ int status_code =
+ capture_shell_command("getprop ro.build.version.sdk", &sdk_version_output_buffer,
+ &sdk_version_error_buffer);
+ if (status_code == 0 && sdk_version_output_buffer.size() > 0) {
+ api_level = strtol((char*)sdk_version_output_buffer.data(), nullptr, 10);
+ }
+ return api_level;
+ }();
return api_level;
}
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
new file mode 100644
index 0000000..cffd4bd
--- /dev/null
+++ b/adb/client/incremental.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "incremental.h"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <openssl/base64.h>
+
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+#ifndef _WIN32
+#include <endian.h>
+#else
+#define be32toh(x) _byteswap_ulong(x)
+#endif
+
+using namespace std::literals;
+
+namespace incremental {
+
+namespace {
+
+static constexpr auto IDSIG = ".idsig"sv;
+
+using android::base::StringPrintf;
+
+using Size = int64_t;
+
+static inline int32_t read_int32(borrowed_fd fd) {
+ int32_t result;
+ ReadFully(fd, &result, sizeof(result));
+ return result;
+}
+
+static inline int32_t read_be_int32(borrowed_fd fd) {
+ return int32_t(be32toh(read_int32(fd)));
+}
+
+static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
+ int32_t be_size = read_int32(fd);
+ int32_t size = int32_t(be32toh(be_size));
+ auto old_size = bytes->size();
+ bytes->resize(old_size + sizeof(be_size) + size);
+ memcpy(bytes->data() + old_size, &be_size, sizeof(be_size));
+ ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size);
+}
+
+static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
+ std::vector<char> result;
+ append_bytes_with_size(fd, &result); // verityRootHash
+ append_bytes_with_size(fd, &result); // v3Digest
+ append_bytes_with_size(fd, &result); // pkcs7SignatureBlock
+ auto tree_size = read_be_int32(fd); // size of the verity tree
+ return {std::move(result), tree_size};
+}
+
+static inline Size verity_tree_size_for_file(Size fileSize) {
+ constexpr int INCFS_DATA_FILE_BLOCK_SIZE = 4096;
+ constexpr int SHA256_DIGEST_SIZE = 32;
+ constexpr int digest_size = SHA256_DIGEST_SIZE;
+ constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
+
+ Size total_tree_block_count = 0;
+
+ auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
+ auto hash_block_count = block_count;
+ for (auto i = 0; hash_block_count > 1; i++) {
+ hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+ total_tree_block_count += hash_block_count;
+ }
+ return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
+}
+
+// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
+ std::string signature_file) {
+ signature_file += IDSIG;
+
+ struct stat st;
+ if (stat(signature_file.c_str(), &st)) {
+ fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+ return {};
+ }
+
+ unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+ return {};
+ }
+
+ auto [signature, tree_size] = read_id_sig_headers(fd);
+ if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
+ fprintf(stderr,
+ "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+ signature_file.c_str(), (long long)tree_size, (long long)expected);
+ return {};
+ }
+
+ size_t base64_len = 0;
+ if (!EVP_EncodedLength(&base64_len, signature.size())) {
+ fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+ return {};
+ }
+ std::string encoded_signature;
+ encoded_signature.resize(base64_len);
+ encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
+ (const uint8_t*)signature.data(), signature.size()));
+
+ return {std::move(fd), std::move(encoded_signature)};
+}
+
+// Send install-incremental to the device along with properly configured file descriptors in
+// streaming format. Once connection established, send all fs-verity tree bytes.
+static unique_fd start_install(const std::vector<std::string>& files) {
+ std::vector<std::string> command_args{"package", "install-incremental"};
+
+ // fd's with positions at the beginning of fs-verity
+ std::vector<unique_fd> signature_fds;
+ signature_fds.reserve(files.size());
+ for (int i = 0, size = files.size(); i < size; ++i) {
+ const auto& file = files[i];
+
+ struct stat st;
+ if (stat(file.c_str(), &st)) {
+ fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+ return {};
+ }
+
+ auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
+ if (!signature_fd.ok()) {
+ return {};
+ }
+
+ auto file_desc =
+ StringPrintf("%s:%lld:%s:%s", android::base::Basename(file).c_str(),
+ (long long)st.st_size, std::to_string(i).c_str(), signature.c_str());
+ command_args.push_back(std::move(file_desc));
+
+ signature_fds.push_back(std::move(signature_fd));
+ }
+
+ std::string error;
+ auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
+ if (connection_fd < 0) {
+ fprintf(stderr, "Failed to run: %s, error: %s\n",
+ android::base::Join(command_args, " ").c_str(), error.c_str());
+ return {};
+ }
+
+ // Pushing verity trees for all installation files.
+ for (auto&& local_fd : signature_fds) {
+ if (!copy_to_file(local_fd.get(), connection_fd.get())) {
+ fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
+ return {};
+ }
+ }
+
+ return connection_fd;
+}
+
+} // namespace
+
+std::optional<Process> install(std::vector<std::string> files) {
+ auto connection_fd = start_install(files);
+ if (connection_fd < 0) {
+ fprintf(stderr, "adb: failed to initiate installation on device.\n");
+ return {};
+ }
+
+ std::string adb_path = android::base::GetExecutablePath();
+
+ auto osh = adb_get_os_handle(connection_fd.get());
+#ifdef _WIN32
+ auto fd_param = std::to_string(reinterpret_cast<intptr_t>(osh));
+#else /* !_WIN32 a.k.a. Unix */
+ auto fd_param = std::to_string(osh);
+#endif
+
+ std::vector<std::string> args(std::move(files));
+ args.insert(args.begin(), {"inc-server", fd_param});
+ auto child = adb_launch_process(adb_path, std::move(args), {connection_fd.get()});
+ if (!child) {
+ fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+ return {};
+ }
+
+ auto killOnExit = [](Process* p) { p->kill(); };
+ std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
+ // TODO: Terminate server process if installation fails.
+ serverKiller.release();
+
+ return child;
+}
+
+} // namespace incremental
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
new file mode 100644
index 0000000..4b9f6bd
--- /dev/null
+++ b/adb/client/incremental.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+
+#include <optional>
+#include <string>
+
+#include "sysdeps.h"
+
+namespace incremental {
+
+std::optional<Process> install(std::vector<std::string> files);
+
+} // namespace incremental
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
new file mode 100644
index 0000000..d9fd77a
--- /dev/null
+++ b/adb/client/incremental_server.cpp
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define TRACE_TAG INCREMENTAL
+
+#include "incremental_server.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+#ifndef _WIN32
+#include <endian.h>
+#else
+#define be64toh(x) _byteswap_uint64(x)
+#define be32toh(x) _byteswap_ulong(x)
+#define be16toh(x) _byteswap_ushort(x)
+#define htobe64(x) _byteswap_uint64(x)
+#define htobe32(x) _byteswap_ulong(x)
+#define htobe16(x) _byteswap_ushort(x)
+#endif
+
+#include <android-base/strings.h>
+#include <lz4.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <fstream>
+#include <thread>
+#include <type_traits>
+#include <unordered_set>
+
+namespace incremental {
+
+static constexpr int kBlockSize = 4096;
+static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
+static constexpr short kCompressionNone = 0;
+static constexpr short kCompressionLZ4 = 1;
+static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
+static constexpr auto kReadBufferSize = 128 * 1024;
+
+using BlockSize = int16_t;
+using FileId = int16_t;
+using BlockIdx = int32_t;
+using NumBlocks = int32_t;
+using CompressionType = int16_t;
+using RequestType = int16_t;
+using ChunkHeader = int32_t;
+using MagicType = uint32_t;
+
+static constexpr MagicType INCR = 0x494e4352; // LE INCR
+
+static constexpr RequestType EXIT = 0;
+static constexpr RequestType BLOCK_MISSING = 1;
+static constexpr RequestType PREFETCH = 2;
+
+static constexpr inline int64_t roundDownToBlockOffset(int64_t val) {
+ return val & ~(kBlockSize - 1);
+}
+
+static constexpr inline int64_t roundUpToBlockOffset(int64_t val) {
+ return roundDownToBlockOffset(val + kBlockSize - 1);
+}
+
+static constexpr inline NumBlocks numBytesToNumBlocks(int64_t bytes) {
+ return roundUpToBlockOffset(bytes) / kBlockSize;
+}
+
+static constexpr inline off64_t blockIndexToOffset(BlockIdx blockIdx) {
+ return static_cast<off64_t>(blockIdx) * kBlockSize;
+}
+
+template <typename T>
+static inline constexpr T toBigEndian(T t) {
+ using unsigned_type = std::make_unsigned_t<T>;
+ if constexpr (std::is_same_v<T, int16_t>) {
+ return htobe16(static_cast<unsigned_type>(t));
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return htobe32(static_cast<unsigned_type>(t));
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return htobe64(static_cast<unsigned_type>(t));
+ } else {
+ return t;
+ }
+}
+
+template <typename T>
+static inline constexpr T readBigEndian(void* data) {
+ using unsigned_type = std::make_unsigned_t<T>;
+ if constexpr (std::is_same_v<T, int16_t>) {
+ return static_cast<T>(be16toh(*reinterpret_cast<unsigned_type*>(data)));
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return static_cast<T>(be32toh(*reinterpret_cast<unsigned_type*>(data)));
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return static_cast<T>(be64toh(*reinterpret_cast<unsigned_type*>(data)));
+ } else {
+ return T();
+ }
+}
+
+// Received from device
+// !Does not include magic!
+struct RequestCommand {
+ RequestType request_type; // 2 bytes
+ FileId file_id; // 2 bytes
+ union {
+ BlockIdx block_idx;
+ NumBlocks num_blocks;
+ }; // 4 bytes
+} __attribute__((packed));
+
+// Placed before actual data bytes of each block
+struct ResponseHeader {
+ FileId file_id; // 2 bytes
+ CompressionType compression_type; // 2 bytes
+ BlockIdx block_idx; // 4 bytes
+ BlockSize block_size; // 2 bytes
+} __attribute__((packed));
+
+// Holds streaming state for a file
+class File {
+ public:
+ // Plain file
+ File(const char* filepath, FileId id, int64_t size, unique_fd fd) : File(filepath, id, size) {
+ this->fd_ = std::move(fd);
+ }
+ int64_t ReadBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed,
+ std::string* error) const {
+ char* buf_ptr = static_cast<char*>(buf);
+ int64_t bytes_read = -1;
+ const off64_t offsetStart = blockIndexToOffset(block_idx);
+ bytes_read = adb_pread(fd_, &buf_ptr[sizeof(ResponseHeader)], kBlockSize, offsetStart);
+ return bytes_read;
+ }
+
+ const unique_fd& RawFd() const { return fd_; }
+
+ std::vector<bool> sentBlocks;
+ NumBlocks sentBlocksCount;
+
+ const char* const filepath;
+ const FileId id;
+ const int64_t size;
+
+ private:
+ File(const char* filepath, FileId id, int64_t size) : filepath(filepath), id(id), size(size) {
+ sentBlocks.resize(numBytesToNumBlocks(size));
+ }
+ unique_fd fd_;
+};
+
+class IncrementalServer {
+ public:
+ IncrementalServer(unique_fd fd, std::vector<File> files)
+ : adb_fd_(std::move(fd)), files_(std::move(files)) {
+ buffer_.reserve(kReadBufferSize);
+ }
+
+ bool Serve();
+
+ private:
+ struct PrefetchState {
+ const File* file;
+ BlockIdx overallIndex = 0;
+ BlockIdx overallEnd = 0;
+
+ PrefetchState(const File& f) : file(&f), overallEnd((BlockIdx)f.sentBlocks.size()) {}
+ PrefetchState(const File& f, BlockIdx start, int count)
+ : file(&f),
+ overallIndex(start),
+ overallEnd(std::min<BlockIdx>(start + count, f.sentBlocks.size())) {}
+
+ bool done() const { return overallIndex >= overallEnd; }
+ };
+
+ bool SkipToRequest(void* buffer, size_t* size, bool blocking);
+ std::optional<RequestCommand> ReadRequest(bool blocking);
+
+ void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
+
+ enum class SendResult { Sent, Skipped, Error };
+ SendResult SendBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+ bool SendDone();
+ void RunPrefetching();
+
+ void Send(const void* data, size_t size, bool flush);
+ void Flush();
+ using TimePoint = decltype(std::chrono::high_resolution_clock::now());
+ bool Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent);
+
+ unique_fd const adb_fd_;
+ std::vector<File> files_;
+
+ // Incoming data buffer.
+ std::vector<char> buffer_;
+
+ std::deque<PrefetchState> prefetches_;
+ int compressed_ = 0, uncompressed_ = 0;
+ long long sentSize_ = 0;
+
+ std::vector<char> pendingBlocks_;
+};
+
+bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
+ while (true) {
+ // Looking for INCR magic.
+ bool magic_found = false;
+ int bcur = 0;
+ for (int bsize = buffer_.size(); bcur + 4 < bsize; ++bcur) {
+ uint32_t magic = be32toh(*(uint32_t*)(buffer_.data() + bcur));
+ if (magic == INCR) {
+ magic_found = true;
+ break;
+ }
+ }
+
+ if (bcur > 0) {
+ // Stream the rest to stderr.
+ fprintf(stderr, "%.*s", bcur, buffer_.data());
+ erase_buffer_head(bcur);
+ }
+
+ if (magic_found && buffer_.size() >= *size + sizeof(INCR)) {
+ // fine, return
+ memcpy(buffer, buffer_.data() + sizeof(INCR), *size);
+ erase_buffer_head(*size + sizeof(INCR));
+ return true;
+ }
+
+ adb_pollfd pfd = {adb_fd_.get(), POLLIN, 0};
+ auto res = adb_poll(&pfd, 1, blocking ? -1 : 0);
+ if (res != 1) {
+ if (res < 0) {
+ fprintf(stderr, "Failed to poll: %s\n", strerror(errno));
+ return false;
+ }
+ *size = 0;
+ return true;
+ }
+
+ auto bsize = buffer_.size();
+ buffer_.resize(kReadBufferSize);
+ int r = adb_read(adb_fd_, buffer_.data() + bsize, kReadBufferSize - bsize);
+ if (r > 0) {
+ buffer_.resize(bsize + r);
+ continue;
+ }
+
+ if (r == -1) {
+ fprintf(stderr, "Failed to read from fd %d: %d. Exit\n", adb_fd_.get(), errno);
+ return true;
+ }
+
+ // socket is closed
+ return false;
+ }
+}
+
+std::optional<RequestCommand> IncrementalServer::ReadRequest(bool blocking) {
+ uint8_t commandBuf[sizeof(RequestCommand)];
+ auto size = sizeof(commandBuf);
+ if (!SkipToRequest(&commandBuf, &size, blocking)) {
+ return {{EXIT}};
+ }
+ if (size < sizeof(RequestCommand)) {
+ return {};
+ }
+ RequestCommand request;
+ request.request_type = readBigEndian<RequestType>(&commandBuf[0]);
+ request.file_id = readBigEndian<FileId>(&commandBuf[2]);
+ request.block_idx = readBigEndian<BlockIdx>(&commandBuf[4]);
+ return request;
+}
+
+auto IncrementalServer::SendBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
+ auto& file = files_[fileId];
+ if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
+ fprintf(stderr, "Failed to read file %s at block %" PRId32 " (past end).\n", file.filepath,
+ blockIdx);
+ return SendResult::Error;
+ }
+ if (file.sentBlocks[blockIdx]) {
+ return SendResult::Skipped;
+ }
+ std::string error;
+ char raw[sizeof(ResponseHeader) + kBlockSize];
+ bool isZipCompressed = false;
+ const int64_t bytesRead = file.ReadBlock(blockIdx, &raw, &isZipCompressed, &error);
+ if (bytesRead < 0) {
+ fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%s).\n", file.filepath, blockIdx,
+ error.c_str());
+ return SendResult::Error;
+ }
+
+ ResponseHeader* header = nullptr;
+ char data[sizeof(ResponseHeader) + kCompressBound];
+ char* compressed = data + sizeof(*header);
+ int16_t compressedSize = 0;
+ if (!isZipCompressed) {
+ compressedSize =
+ LZ4_compress_default(raw + sizeof(*header), compressed, bytesRead, kCompressBound);
+ }
+ int16_t blockSize;
+ if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
+ ++compressed_;
+ blockSize = compressedSize;
+ header = reinterpret_cast<ResponseHeader*>(data);
+ header->compression_type = toBigEndian(kCompressionLZ4);
+ } else {
+ ++uncompressed_;
+ blockSize = bytesRead;
+ header = reinterpret_cast<ResponseHeader*>(raw);
+ header->compression_type = toBigEndian(kCompressionNone);
+ }
+
+ header->file_id = toBigEndian(fileId);
+ header->block_size = toBigEndian(blockSize);
+ header->block_idx = toBigEndian(blockIdx);
+
+ file.sentBlocks[blockIdx] = true;
+ file.sentBlocksCount += 1;
+ Send(header, sizeof(*header) + blockSize, flush);
+ return SendResult::Sent;
+}
+
+bool IncrementalServer::SendDone() {
+ ResponseHeader header;
+ header.file_id = -1;
+ header.compression_type = 0;
+ header.block_idx = 0;
+ header.block_size = 0;
+ Send(&header, sizeof(header), true);
+ return true;
+}
+
+void IncrementalServer::RunPrefetching() {
+ constexpr auto kPrefetchBlocksPerIteration = 128;
+
+ int blocksToSend = kPrefetchBlocksPerIteration;
+ while (!prefetches_.empty() && blocksToSend > 0) {
+ auto& prefetch = prefetches_.front();
+ const auto& file = *prefetch.file;
+ for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
+ if (auto res = SendBlock(file.id, i); res == SendResult::Sent) {
+ --blocksToSend;
+ } else if (res == SendResult::Error) {
+ fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
+ }
+ }
+ if (prefetch.done()) {
+ prefetches_.pop_front();
+ }
+ }
+}
+
+void IncrementalServer::Send(const void* data, size_t size, bool flush) {
+ constexpr auto kChunkFlushSize = 31 * kBlockSize;
+
+ if (pendingBlocks_.empty()) {
+ pendingBlocks_.resize(sizeof(ChunkHeader));
+ }
+ pendingBlocks_.insert(pendingBlocks_.end(), static_cast<const char*>(data),
+ static_cast<const char*>(data) + size);
+ if (flush || pendingBlocks_.size() > kChunkFlushSize) {
+ Flush();
+ }
+}
+
+void IncrementalServer::Flush() {
+ if (pendingBlocks_.empty()) {
+ return;
+ }
+
+ *(ChunkHeader*)pendingBlocks_.data() =
+ toBigEndian<int32_t>(pendingBlocks_.size() - sizeof(ChunkHeader));
+ if (!WriteFdExactly(adb_fd_, pendingBlocks_.data(), pendingBlocks_.size())) {
+ fprintf(stderr, "Failed to write %d bytes\n", int(pendingBlocks_.size()));
+ }
+ sentSize_ += pendingBlocks_.size();
+ pendingBlocks_.clear();
+}
+
+bool IncrementalServer::Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent) {
+ using namespace std::chrono;
+ auto endTime = high_resolution_clock::now();
+ fprintf(stderr,
+ "Connection failed or received exit command. Exit.\n"
+ "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
+ "%d, mb: %.3f\n"
+ "Total time taken: %.3fms\n",
+ missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
+ duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() /
+ 1000.0);
+ return true;
+}
+
+bool IncrementalServer::Serve() {
+ // Initial handshake to verify connection is still alive
+ if (!SendOkay(adb_fd_)) {
+ fprintf(stderr, "Connection is dead. Abort.\n");
+ return false;
+ }
+
+ std::unordered_set<FileId> prefetchedFiles;
+ bool doneSent = false;
+ int missesCount = 0;
+ int missesSent = 0;
+
+ using namespace std::chrono;
+ std::optional<TimePoint> startTime;
+
+ while (true) {
+ if (!doneSent && prefetches_.empty() &&
+ std::all_of(files_.begin(), files_.end(), [](const File& f) {
+ return f.sentBlocksCount == NumBlocks(f.sentBlocks.size());
+ })) {
+ fprintf(stdout, "All files should be loaded. Notifying the device.\n");
+ SendDone();
+ doneSent = true;
+ }
+
+ const bool blocking = prefetches_.empty();
+ if (blocking) {
+ // We've no idea how long the blocking call is, so let's flush whatever is still unsent.
+ Flush();
+ }
+ auto request = ReadRequest(blocking);
+
+ if (!startTime) {
+ startTime = high_resolution_clock::now();
+ }
+
+ if (request) {
+ FileId fileId = request->file_id;
+ BlockIdx blockIdx = request->block_idx;
+
+ switch (request->request_type) {
+ case EXIT: {
+ // Stop everything.
+ return Exit(startTime, missesCount, missesSent);
+ }
+ case BLOCK_MISSING: {
+ ++missesCount;
+ // Sends one single block ASAP.
+ if (fileId < 0 || fileId >= (FileId)files_.size() || blockIdx < 0 ||
+ blockIdx >= (BlockIdx)files_[fileId].sentBlocks.size()) {
+ fprintf(stderr,
+ "Received invalid data request for file_id %" PRId16
+ " block_idx %" PRId32 ".\n",
+ fileId, blockIdx);
+ break;
+ }
+ // fprintf(stderr, "\treading file %d block %04d\n", (int)fileId,
+ // (int)blockIdx);
+ if (auto res = SendBlock(fileId, blockIdx, true); res == SendResult::Error) {
+ fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
+ } else if (res == SendResult::Sent) {
+ ++missesSent;
+ // Make sure we send more pages from this place onward, in case if the OS is
+ // reading a bigger block.
+ prefetches_.emplace_front(files_[fileId], blockIdx + 1, 7);
+ }
+ break;
+ }
+ case PREFETCH: {
+ // Start prefetching for a file
+ if (fileId < 0) {
+ fprintf(stderr,
+ "Received invalid prefetch request for file_id %" PRId16 "\n",
+ fileId);
+ break;
+ }
+ if (!prefetchedFiles.insert(fileId).second) {
+ fprintf(stderr,
+ "Received duplicate prefetch request for file_id %" PRId16 "\n",
+ fileId);
+ break;
+ }
+ D("Received prefetch request for file_id %" PRId16 ".\n", fileId);
+ prefetches_.emplace_back(files_[fileId]);
+ break;
+ }
+ default:
+ fprintf(stderr, "Invalid request %" PRId16 ",%" PRId16 ",%" PRId32 ".\n",
+ request->request_type, fileId, blockIdx);
+ break;
+ }
+ }
+
+ RunPrefetching();
+ }
+}
+
+bool serve(int adb_fd, int argc, const char** argv) {
+ auto connection_fd = unique_fd(adb_fd);
+ if (argc <= 0) {
+ error_exit("inc-server: must specify at least one file.");
+ }
+
+ std::vector<File> files;
+ files.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ auto filepath = argv[i];
+
+ struct stat st;
+ if (stat(filepath, &st)) {
+ fprintf(stderr, "Failed to stat input file %s. Abort.\n", filepath);
+ return {};
+ }
+
+ unique_fd fd(adb_open(filepath, O_RDONLY));
+ if (fd < 0) {
+ error_exit("inc-server: failed to open file '%s'.", filepath);
+ }
+ files.emplace_back(filepath, i, st.st_size, std::move(fd));
+ }
+
+ IncrementalServer server(std::move(connection_fd), std::move(files));
+ printf("Serving...\n");
+ fclose(stdin);
+ fclose(stdout);
+ return server.Serve();
+}
+
+} // namespace incremental
diff --git a/adb/client/incremental_server.h b/adb/client/incremental_server.h
new file mode 100644
index 0000000..53f011e
--- /dev/null
+++ b/adb/client/incremental_server.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+namespace incremental {
+
+// Expecting arguments like:
+// {FILE1 FILE2 ...}
+// Where FILE* are files to serve.
+bool serve(int adbFd, int argc, const char** argv);
+
+} // namespace incremental
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 1a34384..f5811a4 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -25,17 +25,33 @@
#endif
#include <thread>
+#include <vector>
#include <android-base/stringprintf.h>
#include <dns_sd.h>
+#include "adb_client.h"
#include "adb_mdns.h"
#include "adb_trace.h"
#include "fdevent/fdevent.h"
#include "sysdeps.h"
-static DNSServiceRef service_ref;
-static fdevent* service_ref_fde;
+static DNSServiceRef service_refs[kNumADBDNSServices];
+static fdevent* service_ref_fdes[kNumADBDNSServices];
+
+static int adb_DNSServiceIndexByName(const char* regType) {
+ for (int i = 0; i < kNumADBDNSServices; ++i) {
+ if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static bool adb_DNSServiceShouldConnect(const char* regType) {
+ int index = adb_DNSServiceIndexByName(regType);
+ return index == kADBTransportServiceRefIndex;
+}
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
// directly so that the socket is put through the appropriate compatibility
@@ -94,10 +110,16 @@
public:
virtual ~ResolvedService() = default;
- ResolvedService(std::string name, uint32_t interfaceIndex,
- const char* hosttarget, uint16_t port) :
- name_(name),
- port_(port) {
+ ResolvedService(std::string serviceName, std::string regType, uint32_t interfaceIndex,
+ const char* hosttarget, uint16_t port, int version)
+ : serviceName_(serviceName),
+ regType_(regType),
+ hosttarget_(hosttarget),
+ port_(port),
+ sa_family_(0),
+ ip_addr_data_(NULL),
+ serviceVersion_(version) {
+ memset(ip_addr_, 0, sizeof(ip_addr_));
/* TODO: We should be able to get IPv6 support by adding
* kDNSServiceProtocol_IPv6 to the flags below. However, when we do
@@ -116,45 +138,135 @@
} else {
Initialize();
}
+
+ D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
}
void Connect(const sockaddr* address) {
- char ip_addr[INET6_ADDRSTRLEN];
- const void* ip_addr_data;
+ sa_family_ = address->sa_family;
const char* addr_format;
- if (address->sa_family == AF_INET) {
- ip_addr_data =
- &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
+ if (sa_family_ == AF_INET) {
+ ip_addr_data_ = &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
addr_format = "%s:%hu";
- } else if (address->sa_family == AF_INET6) {
- ip_addr_data =
- &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
+ } else if (sa_family_ == AF_INET6) {
+ ip_addr_data_ = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
addr_format = "[%s]:%hu";
- } else { // Should be impossible
+ } else { // Should be impossible
D("mDNS resolved non-IP address.");
return;
}
// Winsock version requires the const cast Because Microsoft.
- if (!inet_ntop(address->sa_family, const_cast<void*>(ip_addr_data),
- ip_addr, INET6_ADDRSTRLEN)) {
+ if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
D("Could not convert IP address to string.");
return;
}
- std::string response;
- connect_device(android::base::StringPrintf(addr_format, ip_addr, port_),
- &response);
- D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_,
- response.c_str());
+ // adb secure service needs to do something different from just
+ // connecting here.
+ if (adb_DNSServiceShouldConnect(regType_.c_str())) {
+ std::string response;
+ connect_device(android::base::StringPrintf(addr_format, ip_addr_, port_), &response);
+ D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
+ ip_addr_, port_, response.c_str());
+ } else {
+ D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+ serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
+ }
+
+ int adbSecureServiceType = serviceIndex();
+ switch (adbSecureServiceType) {
+ case kADBSecurePairingServiceRefIndex:
+ sAdbSecurePairingServices->push_back(this);
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ sAdbSecureConnectServices->push_back(this);
+ break;
+ default:
+ break;
+ }
}
+ int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
+
+ std::string hostTarget() const { return hosttarget_; }
+
+ std::string ipAddress() const { return ip_addr_; }
+
+ uint16_t port() const { return port_; }
+
+ using ServiceRegistry = std::vector<ResolvedService*>;
+
+ static ServiceRegistry* sAdbSecurePairingServices;
+ static ServiceRegistry* sAdbSecureConnectServices;
+
+ static void initAdbSecure();
+
+ static void forEachService(const ServiceRegistry& services, const std::string& hostname,
+ adb_secure_foreach_service_callback cb);
+
private:
- std::string name_;
+ int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
+ std::string serviceName_;
+ std::string regType_;
+ std::string hosttarget_;
const uint16_t port_;
+ int sa_family_;
+ const void* ip_addr_data_;
+ char ip_addr_[INET6_ADDRSTRLEN];
+ int serviceVersion_;
};
+// static
+std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
+
+// static
+std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
+
+// static
+void ResolvedService::initAdbSecure() {
+ if (!sAdbSecurePairingServices) {
+ sAdbSecurePairingServices = new ServiceRegistry;
+ }
+ if (!sAdbSecureConnectServices) {
+ sAdbSecureConnectServices = new ServiceRegistry;
+ }
+}
+
+// static
+void ResolvedService::forEachService(const ServiceRegistry& services,
+ const std::string& wanted_host,
+ adb_secure_foreach_service_callback cb) {
+ initAdbSecure();
+
+ for (auto service : services) {
+ auto hostname = service->hostTarget();
+ auto ip = service->ipAddress();
+ auto port = service->port();
+
+ if (wanted_host == "") {
+ cb(hostname.c_str(), ip.c_str(), port);
+ } else if (hostname == wanted_host) {
+ cb(hostname.c_str(), ip.c_str(), port);
+ }
+ }
+}
+
+// static
+void adb_secure_foreach_pairing_service(const char* host_name,
+ adb_secure_foreach_service_callback cb) {
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
+ host_name ? host_name : "", cb);
+}
+
+// static
+void adb_secure_foreach_connect_service(const char* host_name,
+ adb_secure_foreach_service_callback cb) {
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
+ host_name ? host_name : "", cb);
+}
+
static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
DNSServiceFlags /*flags*/,
uint32_t /*interfaceIndex*/,
@@ -167,6 +279,12 @@
std::unique_ptr<ResolvedService> data(
reinterpret_cast<ResolvedService*>(context));
data->Connect(address);
+
+ // For ADB Secure services, keep those ResolvedService's around
+ // for later processing with secure connection establishment.
+ if (data->serviceIndex() != kADBTransportServiceRefIndex) {
+ data.release();
+ }
}
static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
@@ -182,18 +300,23 @@
class DiscoveredService : public AsyncServiceRef {
public:
- DiscoveredService(uint32_t interfaceIndex, const char* serviceName,
- const char* regtype, const char* domain)
- : serviceName_(serviceName) {
-
+ DiscoveredService(uint32_t interfaceIndex, const char* serviceName, const char* regtype,
+ const char* domain)
+ : serviceName_(serviceName), regType_(regtype) {
DNSServiceErrorType ret =
DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype,
domain, register_resolved_mdns_service,
reinterpret_cast<void*>(this));
- if (ret != kDNSServiceErr_NoError) {
- D("Got %d from DNSServiceResolve.", ret);
- } else {
+ D("DNSServiceResolve for "
+ "interfaceIndex %u "
+ "serviceName %s "
+ "regtype %s "
+ "domain %s "
+ ": %d",
+ interfaceIndex, serviceName, regtype, domain, ret);
+
+ if (ret == kDNSServiceErr_NoError) {
Initialize();
}
}
@@ -202,20 +325,62 @@
return serviceName_.c_str();
}
+ const char* RegType() { return regType_.c_str(); }
+
private:
std::string serviceName_;
+ std::string regType_;
};
-static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
- DNSServiceFlags flags,
- uint32_t interfaceIndex,
- DNSServiceErrorType errorCode,
- const char* fullname,
- const char* hosttarget,
- uint16_t port,
- uint16_t /*txtLen*/,
- const unsigned char* /*txtRecord*/,
- void* context) {
+// Returns the version the device wanted to advertise,
+// or -1 if parsing fails.
+static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
+ if (!txtLen) return -1;
+ if (!txtRecord) return -1;
+
+ // https://tools.ietf.org/html/rfc6763
+ // """
+ // 6.1. General Format Rules for DNS TXT Records
+ //
+ // A DNS TXT record can be up to 65535 (0xFFFF) bytes long. The total
+ // length is indicated by the length given in the resource record header
+ // in the DNS message. There is no way to tell directly from the data
+ // alone how long it is (e.g., there is no length count at the start, or
+ // terminating NULL byte at the end).
+ // """
+
+ // Let's trust the TXT record's length byte
+ // Worst case, it wastes 255 bytes
+ std::vector<char> recordAsString(txtLen + 1, '\0');
+ char* str = recordAsString.data();
+
+ memcpy(str, txtRecord + 1 /* skip the length byte */, txtLen);
+
+ // Check if it's the version key
+ static const char* versionKey = "v=";
+ size_t versionKeyLen = strlen(versionKey);
+
+ if (strncmp(versionKey, str, versionKeyLen)) return -1;
+
+ auto valueStart = str + versionKeyLen;
+
+ long parsedNumber = strtol(valueStart, 0, 10);
+
+ // No valid conversion. Also, 0
+ // is not a valid version.
+ if (!parsedNumber) return -1;
+
+ // Outside bounds of long.
+ if (parsedNumber == LONG_MIN || parsedNumber == LONG_MAX) return -1;
+
+ // Possibly valid version
+ return static_cast<int>(parsedNumber);
+}
+
+static void DNSSD_API register_resolved_mdns_service(
+ DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget, uint16_t port,
+ uint16_t txtLen, const unsigned char* txtRecord, void* context) {
D("Resolved a service.");
std::unique_ptr<DiscoveredService> discovered(
reinterpret_cast<DiscoveredService*>(context));
@@ -225,10 +390,14 @@
return;
}
+ // TODO: Reject certain combinations of invalid or mismatched client and
+ // service versions here before creating anything.
+ // At the moment, there is nothing to reject, so accept everything
+ // as an optimistic default.
+ auto serviceVersion = parse_version_from_txt_record(txtLen, txtRecord);
- auto resolved =
- new ResolvedService(discovered->ServiceName(),
- interfaceIndex, hosttarget, ntohs(port));
+ auto resolved = new ResolvedService(discovered->ServiceName(), discovered->RegType(),
+ interfaceIndex, hosttarget, ntohs(port), serviceVersion);
if (! resolved->Initialized()) {
delete resolved;
@@ -239,19 +408,18 @@
}
}
-static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef,
- DNSServiceFlags flags,
- uint32_t interfaceIndex,
- DNSServiceErrorType errorCode,
- const char* serviceName,
- const char* regtype,
- const char* domain,
- void* /*context*/) {
+static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags flags,
+ uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+ const char* serviceName, const char* regtype,
+ const char* domain, void* /*context*/) {
D("Registering a transport.");
if (errorCode != kDNSServiceErr_NoError) {
D("Got error %d during mDNS browse.", errorCode);
DNSServiceRefDeallocate(sdRef);
- fdevent_destroy(service_ref_fde);
+ int serviceIndex = adb_DNSServiceIndexByName(regtype);
+ if (serviceIndex != -1) {
+ fdevent_destroy(service_ref_fdes[serviceIndex]);
+ }
return;
}
@@ -262,21 +430,27 @@
}
void init_mdns_transport_discovery_thread(void) {
- DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
- register_mdns_transport, nullptr);
+ int errorCodes[kNumADBDNSServices];
- if (errorCode != kDNSServiceErr_NoError) {
- D("Got %d initiating mDNS browse.", errorCode);
- return;
+ for (int i = 0; i < kNumADBDNSServices; ++i) {
+ errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
+ on_service_browsed, nullptr);
+
+ if (errorCodes[i] != kDNSServiceErr_NoError) {
+ D("Got %d browsing for mDNS service %s.", errorCodes[i], kADBDNSServices[i]);
+ }
+
+ if (errorCodes[i] == kDNSServiceErr_NoError) {
+ fdevent_run_on_main_thread([i]() {
+ service_ref_fdes[i] = fdevent_create(adb_DNSServiceRefSockFD(service_refs[i]),
+ pump_service_ref, &service_refs[i]);
+ fdevent_set(service_ref_fdes[i], FDE_READ);
+ });
+ }
}
-
- fdevent_run_on_main_thread([]() {
- service_ref_fde =
- fdevent_create(adb_DNSServiceRefSockFD(service_ref), pump_service_ref, &service_ref);
- fdevent_set(service_ref_fde, FDE_READ);
- });
}
void init_mdns_transport_discovery(void) {
+ ResolvedService::initAdbSecure();
std::thread(init_mdns_transport_discovery_thread).detach();
}
diff --git a/adb/crypto/Android.bp b/adb/crypto/Android.bp
new file mode 100644
index 0000000..da4869a
--- /dev/null
+++ b/adb/crypto/Android.bp
@@ -0,0 +1,85 @@
+// Copyright (C) 2019 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.
+
+cc_defaults {
+ name: "libadb_crypto_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Werror",
+ ],
+
+ compile_multilib: "both",
+
+ srcs: [
+ "key.cpp",
+ "rsa_2048_key.cpp",
+ "x509_generator.cpp",
+ ],
+
+ target: {
+ windows: {
+ compile_multilib: "first",
+ enabled: true,
+ },
+ },
+
+ export_include_dirs: ["include"],
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+ ],
+
+ host_supported: true,
+ recovery_available: true,
+
+ stl: "libc++_static",
+
+ shared_libs: [
+ "libadb_protos",
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ ],
+}
+
+cc_library {
+ name: "libadb_crypto",
+ defaults: ["libadb_crypto_defaults"],
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+
+ static_libs: [
+ "libadb_protos",
+ ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+ name: "libadb_crypto_static",
+ defaults: ["libadb_crypto_defaults"],
+
+ apex_available: [
+ "//apex_available:platform",
+ ],
+
+ static_libs: [
+ "libadb_protos_static",
+ ],
+}
diff --git a/adb/crypto/include/adb/crypto/key.h b/adb/crypto/include/adb/crypto/key.h
new file mode 100644
index 0000000..d9ce69e
--- /dev/null
+++ b/adb/crypto/include/adb/crypto/key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <openssl/evp.h>
+
+#include "key_type.pb.h"
+
+namespace adb {
+namespace crypto {
+
+// Class that represents a public/private key pair.
+class Key {
+ public:
+ explicit Key(bssl::UniquePtr<EVP_PKEY>&& pkey, adb::proto::KeyType type)
+ : pkey_(std::move(pkey)), key_type_(type) {}
+ Key(Key&&) = default;
+ Key& operator=(Key&&) = default;
+
+ EVP_PKEY* GetEvpPkey() const { return pkey_.get(); }
+ adb::proto::KeyType GetKeyType() const { return key_type_; }
+ static std::string ToPEMString(EVP_PKEY* pkey);
+
+ private:
+ bssl::UniquePtr<EVP_PKEY> pkey_;
+ adb::proto::KeyType key_type_;
+}; // Key
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/include/adb/crypto/rsa_2048_key.h b/adb/crypto/include/adb/crypto/rsa_2048_key.h
new file mode 100644
index 0000000..2983a84
--- /dev/null
+++ b/adb/crypto/include/adb/crypto/rsa_2048_key.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include "adb/crypto/key.h"
+
+namespace adb {
+namespace crypto {
+
+// Create a new RSA2048 key pair.
+std::optional<Key> CreateRSA2048Key();
+
+// Generates the public key from the RSA private key.
+bool CalculatePublicKey(std::string* out, RSA* private_key);
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/include/adb/crypto/x509_generator.h b/adb/crypto/include/adb/crypto/x509_generator.h
new file mode 100644
index 0000000..a269243
--- /dev/null
+++ b/adb/crypto/include/adb/crypto/x509_generator.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <openssl/x509v3.h>
+
+namespace adb {
+namespace crypto {
+
+// Generate a X.509 certificate based on the key |pkey|.
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey);
+
+// Convert X509* to PEM string format
+std::string X509ToPEMString(X509* x509);
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/key.cpp b/adb/crypto/key.cpp
new file mode 100644
index 0000000..4d87006
--- /dev/null
+++ b/adb/crypto/key.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "adb/crypto/key.h"
+
+#include <android-base/logging.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+// static
+std::string Key::ToPEMString(EVP_PKEY* pkey) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+ int rc = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr);
+ if (rc != 1) {
+ LOG(ERROR) << "PEM_write_bio_PKCS8PrivateKey failed";
+ return "";
+ }
+
+ BUF_MEM* mem = nullptr;
+ BIO_get_mem_ptr(bio.get(), &mem);
+ if (!mem || !mem->data || !mem->length) {
+ LOG(ERROR) << "BIO_get_mem_ptr failed";
+ return "";
+ }
+
+ return std::string(mem->data, mem->length);
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/rsa_2048_key.cpp b/adb/crypto/rsa_2048_key.cpp
new file mode 100644
index 0000000..7911af9
--- /dev/null
+++ b/adb/crypto/rsa_2048_key.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "adb/crypto/rsa_2048_key.h"
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+std::string get_user_info() {
+ std::string hostname;
+ if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+ char buf[64];
+ if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+ if (hostname.empty()) hostname = "unknown";
+
+ std::string username;
+ if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined(_WIN32)
+ if (username.empty() && getlogin()) username = getlogin();
+#endif
+ if (username.empty()) hostname = "unknown";
+
+ return " " + username + "@" + hostname;
+}
+
+} // namespace
+
+bool CalculatePublicKey(std::string* out, RSA* private_key) {
+ uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+ if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+ LOG(ERROR) << "Failed to convert to public key";
+ return false;
+ }
+
+ size_t expected_length;
+ if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
+ LOG(ERROR) << "Public key too large to base64 encode";
+ return false;
+ }
+
+ out->resize(expected_length);
+ size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
+ sizeof(binary_key_data));
+ out->resize(actual_length);
+ out->append(get_user_info());
+ return true;
+}
+
+std::optional<Key> CreateRSA2048Key() {
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ bssl::UniquePtr<BIGNUM> exponent(BN_new());
+ bssl::UniquePtr<RSA> rsa(RSA_new());
+ if (!pkey || !exponent || !rsa) {
+ LOG(ERROR) << "Failed to allocate key";
+ return std::nullopt;
+ }
+
+ BN_set_word(exponent.get(), RSA_F4);
+ RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
+ EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
+
+ return std::optional<Key>{Key(std::move(pkey), adb::proto::KeyType::RSA_2048)};
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/tests/Android.bp b/adb/crypto/tests/Android.bp
new file mode 100644
index 0000000..b32dcf7
--- /dev/null
+++ b/adb/crypto/tests/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_test {
+ name: "adb_crypto_test",
+ srcs: [
+ "rsa_2048_key_test.cpp",
+ "x509_generator_test.cpp",
+ ],
+
+ compile_multilib: "first",
+
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libprotobuf-cpp-lite",
+ ],
+
+ // Let's statically link them so we don't have to install it onto the
+ // system image for testing.
+ static_libs: [
+ "libadb_crypto_static",
+ "libadb_protos_static",
+ ],
+
+ test_suites: ["device-tests"],
+}
diff --git a/adb/crypto/tests/key_test.cpp b/adb/crypto/tests/key_test.cpp
new file mode 100644
index 0000000..1feb6e8
--- /dev/null
+++ b/adb/crypto/tests/key_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+ auto rsa_2048 = CreateRSA2048Key();
+ EXPECT_NE(rsa_2048, std::nullopt);
+ EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+ ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+ // The public key string format is expected to be: "<pub_key> <host_name>"
+ std::string pub_key_plus_name;
+ auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+ std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+ EXPECT_EQ(split.size(), 2);
+
+ LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+ // Try to sign something and decode it.
+ const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+ std::vector<uint8_t> sig(RSA_size(rsa));
+ unsigned sig_len;
+ EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+ &sig_len, rsa),
+ 1);
+ sig.resize(sig_len);
+
+ {
+ uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+ const std::string& pubkey = split[0];
+ ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+ RSA* key = nullptr;
+ ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+ EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+ sig.data(), sig.size(), key),
+ 1);
+ RSA_free(key);
+ }
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/tests/rsa_2048_key_test.cpp b/adb/crypto/tests/rsa_2048_key_test.cpp
new file mode 100644
index 0000000..1d8880e
--- /dev/null
+++ b/adb/crypto/tests/rsa_2048_key_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+ auto rsa_2048 = CreateRSA2048Key();
+ EXPECT_NE(rsa_2048, std::nullopt);
+ EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+ ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+ // The public key string format is expected to be: "<pub_key> <host_name>"
+ std::string pub_key_plus_name;
+ auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+ std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+ EXPECT_EQ(split.size(), 2);
+
+ LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+ std::string pemString = Key::ToPEMString(rsa_2048->GetEvpPkey());
+ ASSERT_FALSE(pemString.empty());
+
+ // Try to sign something and decode it.
+ const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+ std::vector<uint8_t> sig(RSA_size(rsa));
+ unsigned sig_len;
+ EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+ &sig_len, rsa),
+ 1);
+ sig.resize(sig_len);
+
+ {
+ uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+ const std::string& pubkey = split[0];
+ ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+ RSA* key = nullptr;
+ ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+ EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+ sig.data(), sig.size(), key),
+ 1);
+ RSA_free(key);
+ }
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/tests/x509_generator_test.cpp b/adb/crypto/tests/x509_generator_test.cpp
new file mode 100644
index 0000000..281776b
--- /dev/null
+++ b/adb/crypto/tests/x509_generator_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(X509Generator, Smoke) {
+ auto rsa_2048 = CreateRSA2048Key();
+
+ std::string pub_key_plus_name;
+ auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+ std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+ EXPECT_EQ(split.size(), 2);
+
+ LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+ auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey());
+ ASSERT_NE(x509_cert.get(), nullptr);
+
+ std::string x509_str = X509ToPEMString(x509_cert.get());
+ ASSERT_FALSE(x509_str.empty());
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/crypto/x509_generator.cpp b/adb/crypto/x509_generator.cpp
new file mode 100644
index 0000000..43b8153
--- /dev/null
+++ b/adb/crypto/x509_generator.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "adb/crypto/x509_generator.h"
+
+#include <vector>
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+
+const char kBasicConstraints[] = "critical,CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+bool add_ext(X509* cert, int nid, const char* value) {
+ size_t len = strlen(value) + 1;
+ std::vector<char> mutableValue(value, value + len);
+ X509V3_CTX context;
+
+ X509V3_set_ctx_nodb(&context);
+
+ X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+ X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+ if (!ex) {
+ return false;
+ }
+
+ X509_add_ext(cert, ex, -1);
+ X509_EXTENSION_free(ex);
+ return true;
+}
+
+} // namespace
+
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey) {
+ CHECK(pkey);
+ bssl::UniquePtr<X509> x509(X509_new());
+ if (!x509) {
+ LOG(ERROR) << "Unable to allocate x509 container";
+ return nullptr;
+ }
+ X509_set_version(x509.get(), 2);
+
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
+ X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+ X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+
+ if (!X509_set_pubkey(x509.get(), pkey)) {
+ LOG(ERROR) << "Unable to set x509 public key";
+ return nullptr;
+ }
+
+ X509_NAME* name = X509_get_subject_name(x509.get());
+ if (!name) {
+ LOG(ERROR) << "Unable to get x509 subject name";
+ return nullptr;
+ }
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Adb"), -1, -1, 0);
+ if (!X509_set_issuer_name(x509.get(), name)) {
+ LOG(ERROR) << "Unable to set x509 issuer name";
+ return nullptr;
+ }
+
+ add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+ add_ext(x509.get(), NID_key_usage, kKeyUsage);
+ add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+
+ int bytes = X509_sign(x509.get(), pkey, EVP_sha256());
+ if (bytes <= 0) {
+ LOG(ERROR) << "Unable to sign x509 certificate";
+ return nullptr;
+ }
+
+ return x509;
+}
+
+std::string X509ToPEMString(X509* x509) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+ int rc = PEM_write_bio_X509(bio.get(), x509);
+ if (rc != 1) {
+ LOG(ERROR) << "PEM_write_bio_X509 failed";
+ return "";
+ }
+
+ BUF_MEM* mem = nullptr;
+ BIO_get_mem_ptr(bio.get(), &mem);
+ if (!mem || !mem->data || !mem->length) {
+ LOG(ERROR) << "BIO_get_mem_ptr failed";
+ return "";
+ }
+
+ return std::string(mem->data, mem->length);
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 3530f48..fa98340 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "mdns.h"
#include "adb_mdns.h"
#include "sysdeps.h"
@@ -32,8 +33,8 @@
static std::mutex& mdns_lock = *new std::mutex();
static int port;
-static DNSServiceRef mdns_ref;
-static bool mdns_registered = false;
+static DNSServiceRef mdns_refs[kNumADBDNSServices];
+static bool mdns_registered[kNumADBDNSServices];
static void start_mdns() {
if (android::base::GetProperty("init.svc.mdnsd", "") == "running") {
@@ -60,34 +61,86 @@
}
}
-static void setup_mdns_thread() {
- start_mdns();
+static void register_mdns_service(int index, int port) {
std::lock_guard<std::mutex> lock(mdns_lock);
std::string hostname = "adb-";
hostname += android::base::GetProperty("ro.serialno", "unidentified");
- auto error = DNSServiceRegister(&mdns_ref, 0, 0, hostname.c_str(),
- kADBServiceType, nullptr, nullptr,
- htobe16((uint16_t)port), 0, nullptr,
- mdns_callback, nullptr);
+ // https://tools.ietf.org/html/rfc6763
+ // """
+ // The format of the data within a DNS TXT record is one or more
+ // strings, packed together in memory without any intervening gaps or
+ // padding bytes for word alignment.
+ //
+ // The format of each constituent string within the DNS TXT record is a
+ // single length byte, followed by 0-255 bytes of text data.
+ // """
+ //
+ // Therefore:
+ // 1. Begin with the string length
+ // 2. No null termination
+
+ std::vector<char> txtRecord;
+
+ if (kADBDNSServiceTxtRecords[index]) {
+ size_t txtRecordStringLength = strlen(kADBDNSServiceTxtRecords[index]);
+
+ txtRecord.resize(1 + // length byte
+ txtRecordStringLength // string bytes
+ );
+
+ txtRecord[0] = (char)txtRecordStringLength;
+ memcpy(txtRecord.data() + 1, kADBDNSServiceTxtRecords[index], txtRecordStringLength);
+ }
+
+ auto error = DNSServiceRegister(
+ &mdns_refs[index], 0, 0, hostname.c_str(), kADBDNSServices[index], nullptr, nullptr,
+ htobe16((uint16_t)port), (uint16_t)txtRecord.size(),
+ txtRecord.empty() ? nullptr : txtRecord.data(), mdns_callback, nullptr);
if (error != kDNSServiceErr_NoError) {
- LOG(ERROR) << "Could not register mDNS service (" << error << ").";
- return;
+ LOG(ERROR) << "Could not register mDNS service " << kADBDNSServices[index] << ", error ("
+ << error << ").";
+ mdns_registered[index] = false;
}
- mdns_registered = true;
+ mdns_registered[index] = true;
+
+ LOG(INFO) << "adbd mDNS service " << kADBDNSServices[index]
+ << " registered: " << mdns_registered[index];
}
-static void teardown_mdns() {
+static void unregister_mdns_service(int index) {
std::lock_guard<std::mutex> lock(mdns_lock);
- if (mdns_registered) {
- DNSServiceRefDeallocate(mdns_ref);
+ if (mdns_registered[index]) {
+ DNSServiceRefDeallocate(mdns_refs[index]);
}
}
+static void register_base_mdns_transport() {
+ register_mdns_service(kADBTransportServiceRefIndex, port);
+}
+
+static void setup_mdns_thread() {
+ start_mdns();
+
+ // We will now only set up the normal transport mDNS service
+ // instead of registering all the adb secure mDNS services
+ // in the beginning. This is to provide more privacy/security.
+ register_base_mdns_transport();
+}
+
+// This also tears down any adb secure mDNS services, if they exist.
+static void teardown_mdns() {
+ for (int i = 0; i < kNumADBDNSServices; ++i) {
+ unregister_mdns_service(i);
+ }
+}
+
+// Public interface/////////////////////////////////////////////////////////////
+
void setup_mdns(int port_in) {
port = port_in;
std::thread(setup_mdns_thread).detach();
@@ -95,3 +148,33 @@
// TODO: Make this more robust against a hard kill.
atexit(teardown_mdns);
}
+
+void register_adb_secure_pairing_service(int port) {
+ std::thread([port]() {
+ register_mdns_service(kADBSecurePairingServiceRefIndex, port);
+ }).detach();
+}
+
+void unregister_adb_secure_pairing_service() {
+ std::thread([]() { unregister_mdns_service(kADBSecurePairingServiceRefIndex); }).detach();
+}
+
+bool is_adb_secure_pairing_service_registered() {
+ std::lock_guard<std::mutex> lock(mdns_lock);
+ return mdns_registered[kADBSecurePairingServiceRefIndex];
+}
+
+void register_adb_secure_connect_service(int port) {
+ std::thread([port]() {
+ register_mdns_service(kADBSecureConnectServiceRefIndex, port);
+ }).detach();
+}
+
+void unregister_adb_secure_connect_service() {
+ std::thread([]() { unregister_mdns_service(kADBSecureConnectServiceRefIndex); }).detach();
+}
+
+bool is_adb_secure_connect_service_registered() {
+ std::lock_guard<std::mutex> lock(mdns_lock);
+ return mdns_registered[kADBSecureConnectServiceRefIndex];
+}
diff --git a/adb/daemon/mdns.h b/adb/daemon/mdns.h
index 4c6b1ca..a18093b 100644
--- a/adb/daemon/mdns.h
+++ b/adb/daemon/mdns.h
@@ -19,4 +19,12 @@
void setup_mdns(int port);
+void register_adb_secure_pairing_service(int port);
+void unregister_adb_secure_pairing_service(int port);
+bool is_adb_secure_pairing_service_registered();
+
+void register_adb_secure_connect_service(int port);
+void unregister_adb_secure_connect_service(int port);
+bool is_adb_secure_connect_service_registered();
+
#endif // _DAEMON_MDNS_H_
diff --git a/adb/proto/Android.bp b/adb/proto/Android.bp
new file mode 100644
index 0000000..a7e5d9c
--- /dev/null
+++ b/adb/proto/Android.bp
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 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.
+
+cc_defaults {
+ name: "libadb_protos_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Werror",
+ ],
+
+ compile_multilib: "both",
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: [
+ "adb_known_hosts.proto",
+ "key_type.proto",
+ "pairing.proto",
+ ],
+ target: {
+ windows: {
+ compile_multilib: "first",
+ enabled: true,
+ },
+ },
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+ ],
+
+ stl: "libc++_static",
+
+ host_supported: true,
+ recovery_available: true,
+}
+
+cc_library {
+ name: "libadb_protos",
+ defaults: ["libadb_protos_defaults"],
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+ name: "libadb_protos_static",
+ defaults: ["libadb_protos_defaults"],
+
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
diff --git a/adb/proto/adb_known_hosts.proto b/adb/proto/adb_known_hosts.proto
new file mode 100644
index 0000000..85d1489
--- /dev/null
+++ b/adb/proto/adb_known_hosts.proto
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "AdbKnownHostsProto";
+
+package adb.proto;
+
+// Each known host
+message HostInfo {
+ string guid = 1;
+}
+
+// Protobuf definition for the adb_known_hosts.
+message AdbKnownHosts {
+ repeated HostInfo host_infos = 1;
+}
diff --git a/adb/proto/jarjar-rules.txt b/adb/proto/jarjar-rules.txt
new file mode 100644
index 0000000..4e40637
--- /dev/null
+++ b/adb/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.framework.protobuf.@1
diff --git a/adb/proto/key_type.proto b/adb/proto/key_type.proto
new file mode 100644
index 0000000..ed451c5
--- /dev/null
+++ b/adb/proto/key_type.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "KeyTypeProto";
+
+package adb.proto;
+
+enum KeyType {
+ RSA_2048 = 0;
+}
diff --git a/adb/proto/pairing.proto b/adb/proto/pairing.proto
new file mode 100644
index 0000000..b0be20e
--- /dev/null
+++ b/adb/proto/pairing.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "PairingProto";
+
+package adb.proto;
+
+// The type of packets used in the pairing protocol
+message PairingPacket {
+ enum Type {
+ SPAKE2_MSG = 0;
+ PEER_INFO = 1;
+ }
+}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 0c5a6b4..2318395 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -267,6 +267,39 @@
#define getcwd adb_getcwd
+// A very simple wrapper over a launched child process
+class Process {
+ public:
+ constexpr explicit Process(HANDLE h = nullptr) : h_(h) {}
+ ~Process() { close(); }
+ constexpr explicit operator bool() const { return h_ != nullptr; }
+
+ void wait() {
+ if (*this) {
+ ::WaitForSingleObject(h_, INFINITE);
+ close();
+ }
+ }
+ void kill() {
+ if (*this) {
+ ::TerminateProcess(h_, -1);
+ }
+ }
+
+ private:
+ void close() {
+ if (*this) {
+ ::CloseHandle(h_);
+ h_ = nullptr;
+ }
+ }
+
+ HANDLE h_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit = {});
+
// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
// passed to main().
class NarrowArgs {
@@ -432,11 +465,11 @@
return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
-static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
+static __inline__ int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
#if defined(__APPLE__)
- return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
+ return TEMP_FAILURE_RETRY(pread(fd.get(), buf, len, offset));
#else
- return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
+ return TEMP_FAILURE_RETRY(pread64(fd.get(), buf, len, offset));
#endif
}
@@ -612,6 +645,32 @@
return fd.get();
}
+// A very simple wrapper over a launched child process
+class Process {
+ public:
+ constexpr explicit Process(pid_t pid) : pid_(pid) {}
+ constexpr explicit operator bool() const { return pid_ >= 0; }
+
+ void wait() {
+ if (*this) {
+ int status;
+ ::waitpid(pid_, &status, 0);
+ pid_ = -1;
+ }
+ }
+ void kill() {
+ if (*this) {
+ ::kill(pid_, SIGTERM);
+ }
+ }
+
+ private:
+ pid_t pid_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit = {});
+
#endif /* !_WIN32 */
static inline void disable_tcp_nagle(borrowed_fd fd) {
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 3fdc917..e565706 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -56,3 +56,37 @@
return true;
}
+
+static __inline__ void disable_close_on_exec(borrowed_fd fd) {
+ const auto oldFlags = fcntl(fd.get(), F_GETFD);
+ const auto newFlags = (oldFlags & ~FD_CLOEXEC);
+ if (newFlags != oldFlags) {
+ fcntl(fd.get(), F_SETFD, newFlags);
+ }
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit) {
+ const auto pid = fork();
+ if (pid != 0) {
+ // parent, includes the case when failed to fork()
+ return Process(pid);
+ }
+ // child
+ std::vector<std::string> copies;
+ copies.reserve(args.size() + 1);
+ copies.emplace_back(executable);
+ copies.insert(copies.end(), std::make_move_iterator(args.begin()),
+ std::make_move_iterator(args.end()));
+
+ std::vector<char*> rawArgs;
+ rawArgs.reserve(copies.size() + 1);
+ for (auto&& str : copies) {
+ rawArgs.push_back(str.data());
+ }
+ rawArgs.push_back(nullptr);
+ for (auto fd : fds_to_inherit) {
+ disable_close_on_exec(fd);
+ }
+ exit(execv(copies.front().data(), rawArgs.data()));
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index d9cc36f..e33d51c 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2771,6 +2771,66 @@
return buf;
}
+void enable_inherit(borrowed_fd fd) {
+ auto osh = adb_get_os_handle(fd);
+ const auto h = reinterpret_cast<HANDLE>(osh);
+ ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+}
+
+void disable_inherit(borrowed_fd fd) {
+ auto osh = adb_get_os_handle(fd);
+ const auto h = reinterpret_cast<HANDLE>(osh);
+ ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0);
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit) {
+ std::wstring wexe;
+ if (!android::base::UTF8ToWide(executable.data(), executable.size(), &wexe)) {
+ return Process();
+ }
+
+ std::wstring wargs = L"\"" + wexe + L"\"";
+ std::wstring warg;
+ for (auto arg : args) {
+ warg.clear();
+ if (!android::base::UTF8ToWide(arg.data(), arg.size(), &warg)) {
+ return Process();
+ }
+ wargs += L" \"";
+ wargs += warg;
+ wargs += L'\"';
+ }
+
+ STARTUPINFOW sinfo = {sizeof(sinfo)};
+ PROCESS_INFORMATION pinfo = {};
+
+ // TODO: use the Vista+ API to pass the list of inherited handles explicitly;
+ // see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
+ for (auto fd : fds_to_inherit) {
+ enable_inherit(fd);
+ }
+ const auto created = CreateProcessW(wexe.c_str(), wargs.data(),
+ nullptr, // process attributes
+ nullptr, // thread attributes
+ fds_to_inherit.size() > 0, // inherit any handles?
+ 0, // flags
+ nullptr, // environment
+ nullptr, // current directory
+ &sinfo, // startup info
+ &pinfo);
+ for (auto fd : fds_to_inherit) {
+ disable_inherit(fd);
+ }
+
+ if (!created) {
+ return Process();
+ }
+
+ ::CloseHandle(pinfo.hThread);
+ return Process(pinfo.hProcess);
+}
+
// The SetThreadDescription API was brought in version 1607 of Windows 10.
typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
diff --git a/adb/tls/Android.bp b/adb/tls/Android.bp
new file mode 100644
index 0000000..49833ff
--- /dev/null
+++ b/adb/tls/Android.bp
@@ -0,0 +1,75 @@
+// Copyright (C) 2020 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.
+
+cc_defaults {
+ name: "libadb_tls_connection_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Werror",
+ ],
+
+ compile_multilib: "both",
+
+ srcs: [
+ "adb_ca_list.cpp",
+ "tls_connection.cpp",
+ ],
+ target: {
+ windows: {
+ compile_multilib: "first",
+ enabled: true,
+ },
+ },
+ export_include_dirs: ["include"],
+
+ host_supported: true,
+ recovery_available: true,
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+ ],
+
+ stl: "libc++_static",
+
+ static_libs: [
+ "libbase",
+ ],
+ shared_libs: [
+ "libcrypto",
+ "liblog",
+ "libssl",
+ ],
+}
+
+cc_library {
+ name: "libadb_tls_connection",
+ defaults: ["libadb_tls_connection_defaults"],
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+ name: "libadb_tls_connection_static",
+ defaults: ["libadb_tls_connection_defaults"],
+
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
diff --git a/adb/tls/adb_ca_list.cpp b/adb/tls/adb_ca_list.cpp
new file mode 100644
index 0000000..36afe42
--- /dev/null
+++ b/adb/tls/adb_ca_list.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "adb/tls/adb_ca_list.h"
+
+#include <iomanip>
+#include <sstream>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <openssl/ssl.h>
+
+namespace adb {
+namespace tls {
+
+namespace {
+
+// CA issuer identifier to distinguished embedded keys. Also has version
+// information appended to the end of the string (e.g. "AdbKey-0").
+static constexpr int kAdbKeyIdentifierNid = NID_organizationName;
+static constexpr char kAdbKeyIdentifierV0[] = "AdbKey-0";
+
+// Where we store the actual data
+static constexpr int kAdbKeyValueNid = NID_commonName;
+
+// TODO: Remove this once X509_NAME_add_entry_by_NID is fixed to use const unsigned char*
+// https://boringssl-review.googlesource.com/c/boringssl/+/39764
+int X509_NAME_add_entry_by_NID_const(X509_NAME* name, int nid, int type, const unsigned char* bytes,
+ int len, int loc, int set) {
+ return X509_NAME_add_entry_by_NID(name, nid, type, const_cast<unsigned char*>(bytes), len, loc,
+ set);
+}
+
+bool IsHexDigit(char c) {
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+}
+
+// Wrapper around X509_NAME_get_text_by_NID that first calculates the size
+// of the string. Returns empty string on failure.
+std::optional<std::string> GetX509NameTextByNid(X509_NAME* name, int nid) {
+ // |len| is the len of the text excluding the final null
+ int len = X509_NAME_get_text_by_NID(name, nid, nullptr, -1);
+ if (len <= 0) {
+ return std::nullopt;
+ }
+
+ // Include the space for the final null byte
+ std::vector<char> buf(len + 1, '\0');
+ CHECK(X509_NAME_get_text_by_NID(name, nid, buf.data(), buf.size()));
+ return std::make_optional(std::string(buf.data()));
+}
+
+} // namespace
+
+// Takes an encoded public key and generates a X509_NAME that can be used in
+// TlsConnection::SetClientCAList(), to allow the client to figure out which of
+// its keys it should try to use in the TLS handshake.
+bssl::UniquePtr<X509_NAME> CreateCAIssuerFromEncodedKey(std::string_view key) {
+ // "O=AdbKey-0;CN=<key>;"
+ CHECK(!key.empty());
+
+ std::string identifier = kAdbKeyIdentifierV0;
+ bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
+ CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyIdentifierNid, MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>(identifier.data()),
+ identifier.size(), -1, 0));
+
+ CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyValueNid, MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>(key.data()), key.size(),
+ -1, 0));
+ return name;
+}
+
+// Parses a CA issuer and returns the encoded key, if any.
+std::optional<std::string> ParseEncodedKeyFromCAIssuer(X509_NAME* issuer) {
+ CHECK(issuer);
+
+ auto buf = GetX509NameTextByNid(issuer, kAdbKeyIdentifierNid);
+ if (!buf) {
+ return std::nullopt;
+ }
+
+ // Check for supported versions
+ if (*buf == kAdbKeyIdentifierV0) {
+ return GetX509NameTextByNid(issuer, kAdbKeyValueNid);
+ }
+ return std::nullopt;
+}
+
+std::string SHA256BitsToHexString(std::string_view sha256) {
+ CHECK_EQ(sha256.size(), static_cast<size_t>(SHA256_DIGEST_LENGTH));
+ std::stringstream ss;
+ auto* u8 = reinterpret_cast<const uint8_t*>(sha256.data());
+ ss << std::uppercase << std::setfill('0') << std::hex;
+ // Convert to hex-string representation
+ for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
+ // Need to cast to something bigger than one byte, or
+ // stringstream will interpret it as a char value.
+ ss << std::setw(2) << static_cast<uint16_t>(u8[i]);
+ }
+ return ss.str();
+}
+
+std::optional<std::string> SHA256HexStringToBits(std::string_view sha256_str) {
+ if (sha256_str.size() != SHA256_DIGEST_LENGTH * 2) {
+ return std::nullopt;
+ }
+
+ std::string result;
+ for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
+ auto bytestr = std::string(sha256_str.substr(i * 2, 2));
+ if (!IsHexDigit(bytestr[0]) || !IsHexDigit(bytestr[1])) {
+ LOG(ERROR) << "SHA256 string has invalid non-hex chars";
+ return std::nullopt;
+ }
+ result += static_cast<char>(std::stol(bytestr, nullptr, 16));
+ }
+ return result;
+}
+
+} // namespace tls
+} // namespace adb
diff --git a/adb/tls/include/adb/tls/adb_ca_list.h b/adb/tls/include/adb/tls/adb_ca_list.h
new file mode 100644
index 0000000..a1ab9a7
--- /dev/null
+++ b/adb/tls/include/adb/tls/adb_ca_list.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <openssl/base.h>
+#include <optional>
+#include <string>
+
+// These APIs is used to embed adbd's known public keys into client-allowed CA
+// issuer list that can indicate to the client which key to use.
+namespace adb {
+namespace tls {
+
+// Takes an encoded public key and generates a X509_NAME that can be used in
+// TlsConnection::SetClientCAList(), to allow the client to figure out which of
+// its keys it should try to use in the TLS handshake. This is guaranteed to
+// return a valid X509_NAME, given a non-empty key.
+bssl::UniquePtr<X509_NAME> CreateCAIssuerFromEncodedKey(std::string_view key);
+
+// Parses a CA issuer and returns the encoded key, if any. On failure, returns
+// nullopt.
+std::optional<std::string> ParseEncodedKeyFromCAIssuer(X509_NAME* issuer);
+
+// Converts SHA256 bits to a hex string representation. |sha256| must be exactly
+// |SHA256_DIGEST_LENGTH| in size.
+std::string SHA256BitsToHexString(std::string_view sha256);
+
+// Converts a valid SHA256 hex string to the actual bits. Returns nullopt on
+// failure.
+std::optional<std::string> SHA256HexStringToBits(std::string_view sha256_str);
+
+} // namespace tls
+} // namespace adb
diff --git a/adb/tls/include/adb/tls/tls_connection.h b/adb/tls/include/adb/tls/tls_connection.h
new file mode 100644
index 0000000..bc5b98a
--- /dev/null
+++ b/adb/tls/include/adb/tls/tls_connection.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string_view>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+namespace adb {
+namespace tls {
+
+class TlsConnection {
+ public:
+ // This class will require both client and server to exchange valid
+ // certificates.
+ enum class Role {
+ Server,
+ Client,
+ };
+
+ enum class TlsError : uint8_t {
+ Success = 0,
+ // An error indicating that we rejected the peer's certificate.
+ CertificateRejected,
+ // An error indicating that the peer rejected our certificate.
+ PeerRejectedCertificate,
+ // Add more if needed
+ UnknownFailure,
+ };
+
+ using CertVerifyCb = std::function<int(X509_STORE_CTX*)>;
+ using SetCertCb = std::function<int(SSL*)>;
+
+ virtual ~TlsConnection() = default;
+
+ // Adds a trusted certificate to the list for the SSL connection.
+ // During the handshake phase, it will check the list of trusted certificates.
+ // The connection will fail if the peer's certificate is not in the list. If
+ // you would like to accept any certificate, use #SetCertVerifyCallback and
+ // set your callback to always return 1.
+ //
+ // Returns true if |cert| was successfully added, false otherwise.
+ virtual bool AddTrustedCertificate(std::string_view cert) = 0;
+
+ // Sets a custom certificate verify callback. |cb| must return 1 if the
+ // certificate is trusted. Otherwise, return 0 if not.
+ virtual void SetCertVerifyCallback(CertVerifyCb cb) = 0;
+
+ // Configures a client |ca_list| that the server sends to the client in the
+ // CertificateRequest message.
+ virtual void SetClientCAList(STACK_OF(X509_NAME) * ca_list) = 0;
+
+ // Sets a callback that will be called to select a certificate. See
+ // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_cert_cb
+ // for more details.
+ virtual void SetCertificateCallback(SetCertCb cb) = 0;
+
+ // Exports a value derived from the master secret used in the TLS
+ // connection. This value should be used alongside any PAKE to ensure the
+ // peer is the intended peer. |length| is the requested length for the
+ // keying material. This is only valid after |DoHandshake| succeeds.
+ virtual std::vector<uint8_t> ExportKeyingMaterial(size_t length) = 0;
+
+ // Enable client-side check on whether server accepted the handshake. In TLS
+ // 1.3, client will not know the server rejected the handshake until after
+ // performing a read operation. Basically, this will perform an
+ // SSL_peek right after the handshake and see whether that succeeds.
+ //
+ // IMPORTANT: this will only work if the protocol is a server-speaks-first
+ // type. Enabling this for the server is a no-op. This is disabled by
+ // default.
+ virtual void EnableClientPostHandshakeCheck(bool enable) = 0;
+
+ // Starts the handshake process. Returns TlsError::Success if handshake
+ // succeeded.
+ virtual TlsError DoHandshake() = 0;
+
+ // Reads |size| bytes and returns the data. The returned data has either
+ // size |size| or zero, in which case the read failed.
+ virtual std::vector<uint8_t> ReadFully(size_t size) = 0;
+
+ // Overloaded ReadFully method, which accepts a buffer for writing in.
+ // Returns true iff exactly |size| amount of data was written into |buf|,
+ // false otherwise.
+ virtual bool ReadFully(void* buf, size_t size) = 0;
+
+ // Writes |size| bytes. Returns true if all |size| bytes were read.
+ // Returns false otherwise.
+ virtual bool WriteFully(std::string_view data) = 0;
+
+ // Create a new TlsConnection instance. |cert| and |priv_key| cannot be
+ // empty.
+ static std::unique_ptr<TlsConnection> Create(Role role, std::string_view cert,
+ std::string_view priv_key,
+ android::base::borrowed_fd fd);
+
+ // Helper to set the certificate and key strings to a SSL client/server.
+ // Useful when in the set-certificate callback.
+ static bool SetCertAndKey(SSL* ssl, std::string_view cert_chain, std::string_view priv_key);
+
+ protected:
+ TlsConnection() = default;
+}; // TlsConnection
+
+} // namespace tls
+} // namespace adb
diff --git a/adb/tls/tests/Android.bp b/adb/tls/tests/Android.bp
new file mode 100644
index 0000000..198de58
--- /dev/null
+++ b/adb/tls/tests/Android.bp
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_test {
+ name: "adb_tls_connection_test",
+ srcs: [
+ "adb_ca_list_test.cpp",
+ "tls_connection_test.cpp",
+ ],
+
+ compile_multilib: "first",
+
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libssl",
+ ],
+
+ // Let's statically link them so we don't have to install it onto the
+ // system image for testing.
+ static_libs: [
+ "libadb_crypto_static",
+ "libadb_protos_static",
+ "libadb_tls_connection_static",
+ ],
+
+ test_suites: ["device-tests"],
+}
diff --git a/adb/tls/tests/adb_ca_list_test.cpp b/adb/tls/tests/adb_ca_list_test.cpp
new file mode 100644
index 0000000..c727e5f
--- /dev/null
+++ b/adb/tls/tests/adb_ca_list_test.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define LOG_TAG "AdbCAListTest"
+
+#include <gtest/gtest.h>
+
+#include <adb/tls/adb_ca_list.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <openssl/ssl.h>
+
+namespace adb {
+namespace tls {
+
+class AdbCAListTest : public testing::Test {
+ protected:
+ virtual void SetUp() override {}
+
+ virtual void TearDown() override {}
+};
+
+TEST_F(AdbCAListTest, SHA256BitsToHexString_BadParam) {
+ // Should crash if not exactly SHA256_DIGEST_LENGTH size
+ ASSERT_DEATH(
+ {
+ // empty
+ std::string sha;
+ SHA256BitsToHexString(sha);
+ },
+ "");
+ ASSERT_DEATH(
+ {
+ std::string sha(1, 0x80);
+ SHA256BitsToHexString(sha);
+ },
+ "");
+ ASSERT_DEATH(
+ {
+ std::string sha(SHA256_DIGEST_LENGTH - 1, 0x80);
+ SHA256BitsToHexString(sha);
+ },
+ "");
+ ASSERT_DEATH(
+ {
+ std::string sha(SHA256_DIGEST_LENGTH + 1, 0x80);
+ SHA256BitsToHexString(sha);
+ },
+ "");
+}
+
+TEST_F(AdbCAListTest, SHA256HexStringToBits_BadParam) {
+ {
+ // empty
+ std::string sha_str;
+ auto res = SHA256HexStringToBits(sha_str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string sha_str(1, 'a');
+ auto res = SHA256HexStringToBits(sha_str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string sha_str(SHA256_DIGEST_LENGTH * 2 - 1, 'a');
+ auto res = SHA256HexStringToBits(sha_str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string sha_str(SHA256_DIGEST_LENGTH * 2 + 1, 'a');
+ auto res = SHA256HexStringToBits(sha_str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ // Non-hex chars
+ std::string sha_str(SHA256_DIGEST_LENGTH * 2, 'a');
+ sha_str[32] = 'x';
+ auto res = SHA256HexStringToBits(sha_str);
+ EXPECT_FALSE(res.has_value());
+ }
+}
+
+TEST_F(AdbCAListTest, SHA256BitsToHexString_ValidParam) {
+ uint8_t ct = 0;
+ // Test every possible byte
+ std::vector<std::string> expectedStr = {
+ "000102030405060708090A0B0C0D0E0F"
+ "101112131415161718191A1B1C1D1E1F",
+
+ "202122232425262728292A2B2C2D2E2F"
+ "303132333435363738393A3B3C3D3E3F",
+
+ "404142434445464748494A4B4C4D4E4F"
+ "505152535455565758595A5B5C5D5E5F",
+
+ "606162636465666768696A6B6C6D6E6F"
+ "707172737475767778797A7B7C7D7E7F",
+
+ "808182838485868788898A8B8C8D8E8F"
+ "909192939495969798999A9B9C9D9E9F",
+
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
+ "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF",
+
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
+ "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF",
+
+ "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
+ "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF",
+ };
+
+ for (auto& expected : expectedStr) {
+ std::string sha;
+ while (sha.size() < SHA256_DIGEST_LENGTH) {
+ sha += ct++;
+ }
+
+ auto sha_str = SHA256BitsToHexString(sha);
+ EXPECT_EQ(expected, sha_str);
+
+ // try to convert back to bits
+ auto out_sha = SHA256HexStringToBits(sha_str);
+ ASSERT_TRUE(out_sha.has_value());
+ EXPECT_EQ(*out_sha, sha);
+ }
+}
+
+TEST_F(AdbCAListTest, CreateCAIssuerFromEncodedKey_EmptyKey) {
+ ASSERT_DEATH({ auto issuer = CreateCAIssuerFromEncodedKey(""); }, "");
+}
+
+TEST_F(AdbCAListTest, Smoke) {
+ {
+ std::string key =
+ "A45BC1FF6C89BF0E"
+ "65F9BA153FBC9876"
+ "4969B4113F1CF878"
+ "EEF9BF1C3F9C9227";
+ auto issuer = CreateCAIssuerFromEncodedKey(key);
+ ASSERT_NE(issuer, nullptr);
+
+ // Try to parse the encoded key out of the X509_NAME
+ auto out_key = ParseEncodedKeyFromCAIssuer(issuer.get());
+ ASSERT_TRUE(out_key.has_value());
+ EXPECT_EQ(key, *out_key);
+ }
+}
+
+} // namespace tls
+} // namespace adb
diff --git a/adb/tls/tests/tls_connection_test.cpp b/adb/tls/tests/tls_connection_test.cpp
new file mode 100644
index 0000000..27bc1c9
--- /dev/null
+++ b/adb/tls/tests/tls_connection_test.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define LOG_TAG "AdbWifiTlsConnectionTest"
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#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/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <openssl/ssl.h>
+
+using namespace adb::crypto;
+
+namespace adb {
+namespace tls {
+
+using android::base::unique_fd;
+using TlsError = TlsConnection::TlsError;
+
+// Test X.509 certificates (RSA 2048)
+static const std::string kTestRsa2048ServerCert =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+ "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NVoX\n"
+ "DTMwMDExODIyMjU1NVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+ "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8E\n"
+ "2Ck9TfuKlz7wqWdMfknjZ1luFDp2IHxAUZzh/F6jeI2dOFGAjpeloSnGOE86FIaT\n"
+ "d1EvpyTh7nBwbrLZAA6XFZTo7Bl6BdNOQdqb2d2+cLEN0inFxqUIycevRtohUE1Y\n"
+ "FHM9fg442X1jOTWXjDZWeiqFWo95paAPhzm6pWqfJK1+YKfT1LsWZpYqJGGQE5pi\n"
+ "C3qOBYYgFpoXMxTYJNoZo3uOYEdM6upc8/vh15nMgIxX/ymJxEY5BHPpZPPWjXLg\n"
+ "BfzVaV9fUfv0JT4HQ4t2WvxC3cD/UsjWp2a6p454uUp2ENrANa+jRdRJepepg9D2\n"
+ "DKsx9L8zjc5Obqexrt0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+ "Af8EBAMCAYYwHQYDVR0OBBYEFDFW+8GTErwoZN5Uu9KyY4QdGYKpMA0GCSqGSIb3\n"
+ "DQEBCwUAA4IBAQBCDEn6SHXGlq5TU7J8cg1kRPd9bsJW+0hDuKSq0REXDkl0PcBf\n"
+ "fy282Agg9enKPPKmnpeQjM1dmnxdM8tT8LIUbMl779i3fn6v9HJVB+yG4gmRFThW\n"
+ "c+AGlBnrIT820cX/gU3h3R3FTahfsq+1rrSJkEgHyuC0HYeRyveSckBdaEOLvx0S\n"
+ "toun+32JJl5hWydpUUZhE9Mbb3KHBRM2YYZZU9JeJ08Apjl+3lRUeMAUwI5fkAAu\n"
+ "z/1SqnuGL96bd8P5ixdkA1+rF8FPhodGcq9mQOuUGP9g5HOXjaNoJYvwVRUdLeGh\n"
+ "cP/ReOTwQIzM1K5a83p8cX8AGGYmM7dQp7ec\n"
+ "-----END CERTIFICATE-----\n";
+
+static const std::string kTestRsa2048ServerPrivKey =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvBNgpPU37ipc+\n"
+ "8KlnTH5J42dZbhQ6diB8QFGc4fxeo3iNnThRgI6XpaEpxjhPOhSGk3dRL6ck4e5w\n"
+ "cG6y2QAOlxWU6OwZegXTTkHam9ndvnCxDdIpxcalCMnHr0baIVBNWBRzPX4OONl9\n"
+ "Yzk1l4w2VnoqhVqPeaWgD4c5uqVqnyStfmCn09S7FmaWKiRhkBOaYgt6jgWGIBaa\n"
+ "FzMU2CTaGaN7jmBHTOrqXPP74deZzICMV/8picRGOQRz6WTz1o1y4AX81WlfX1H7\n"
+ "9CU+B0OLdlr8Qt3A/1LI1qdmuqeOeLlKdhDawDWvo0XUSXqXqYPQ9gyrMfS/M43O\n"
+ "Tm6nsa7dAgMBAAECggEAFCS2bPdUKIgjbzLgtHW+hT+J2hD20rcHdyAp+dNH/2vI\n"
+ "yLfDJHJA4chGMRondKA704oDw2bSJxxlG9t83326lB35yxPhye7cM8fqgWrK8PVl\n"
+ "tU22FhO1ZgeJvb9OeXWNxKZyDW9oOOJ8eazNXVMuEo+dFj7B6l3MXQyHJPL2mJDm\n"
+ "u9ofFLdypX+gJncVO0oW0FNJnEUn2MMwHDNlo7gc4WdQuidPkuZItKRGcB8TTGF3\n"
+ "Ka1/2taYdTQ4Aq//Z84LlFvE0zD3T4c8LwYYzOzD4gGGTXvft7vSHzIun1S8YLRS\n"
+ "dEKXdVjtaFhgH3uUe4j+1b/vMvSHeoGBNX/G88GD+wKBgQDWUYVlMVqc9HD2IeYi\n"
+ "EfBcNwAJFJkh51yAl5QbUBgFYgFJVkkS/EDxEGFPvEmI3/pAeQFHFY13BI466EPs\n"
+ "o8Z8UUwWDp+Z1MFHHKQKnFakbsZbZlbqjJ9VJsqpezbpWhMHTOmcG0dmE7rf0lyM\n"
+ "eQv9slBB8qp2NEUs5Of7f2C2bwKBgQDRDq4nUuMQF1hbjM05tGKSIwkobmGsLspv\n"
+ "TMhkM7fq4RpbFHmbNgsFqMhcqYZ8gY6/scv5KCuAZ4yHUkbqwf5h+QCwrJ4uJeUJ\n"
+ "ZgJfHus2mmcNSo8FwSkNoojIQtzcbJav7bs2K9VTuertk/i7IJLApU4FOZZ5pghN\n"
+ "EXu0CZF1cwKBgDWFGhjRIF29tU/h20R60llU6s9Zs3wB+NmsALJpZ/ZAKS4VPB5f\n"
+ "nCAXBRYSYRKrTCU5kpYbzb4BBzuysPOxWmnFK4j+keCqfrGxd02nCQP7HdHJVr8v\n"
+ "6sIq88UrHeVcNxBFprjzHvtgxfQK5k22FMZ/9wbhAKyQFQ5HA5+MiaxFAoGAIcZZ\n"
+ "ZIkDninnYIMS9OursShv5lRO+15j3i9tgKLKZ+wOMgDQ1L6acUOfezj4PU1BHr8+\n"
+ "0PYocQpJreMhCfRlgLaV4fVBaPs+UZJld7CrF5tCYudUy/01ALrtlk0XGZWBktK5\n"
+ "mDrksC4tQkzRtonAq9cJD9cJ9IVaefkFH0UcdvkCgYBpZj50VLeGhnHHBnkJRlV1\n"
+ "fV+/P6PAq6RtqjA6O9Qdaoj5V3w2d63aQcQXQLJjH2BBmtCIy47r04rFvZpbCxP7\n"
+ "NH/OnK9NHpk2ucRTe8TAnVbvF/TZzPJoIxAO/D3OWaW6df4R8en8u6GYzWFglAyT\n"
+ "sydGT8yfWD1FYUWgfrVRbg==\n"
+ "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048ClientCert =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+ "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NloX\n"
+ "DTMwMDExODIyMjU1NlowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+ "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI3a\n"
+ "EXh1S5FTbet7JVONswffRPaekdIK53cb8SnAbSO9X5OLA4zGwdkrBvDTsd96SKrp\n"
+ "JxmoNOE1DhbZh05KPlWAPkGKacjGWaz+S7biDOL0I6aaLbTlU/il1Ub9olPSBVUx\n"
+ "0nhdtEFgIOzddnP6/1KmyIIeRxS5lTKeg4avqUkZNXkz/wL1dHBFL7FNFf0SCcbo\n"
+ "tsub/deFbjZ27LTDN+SIBgFttTNqC5NTvoBAoMdyCOAgNYwaHO+fKiK3edfJieaw\n"
+ "7HD8qqmQxcpCtRlA8CUPj7GfR+WHiCJmlevhnkFXCo56R1BS0F4wuD4KPdSWt8gc\n"
+ "27ejH/9/z2cKo/6SLJMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+ "Af8EBAMCAYYwHQYDVR0OBBYEFO/Mr5ygqqpyU/EHM9v7RDvcqaOkMA0GCSqGSIb3\n"
+ "DQEBCwUAA4IBAQAH33KMouzF2DYbjg90KDrDQr4rq3WfNb6P743knxdUFuvb+40U\n"
+ "QjC2OJZHkSexH7wfG/y6ic7vfCfF4clNs3QvU1lEjOZC57St8Fk7mdNdsWLwxEMD\n"
+ "uePFz0dvclSxNUHyCVMqNxddzQYzxiDWQRmXWrUBliMduQqEQelcxW2yDtg8bj+s\n"
+ "aMpR1ra9scaD4jzIZIIxLoOS9zBMuNRbgP217sZrniyGMhzoI1pZ/izN4oXpyH7O\n"
+ "THuaCzzRT3ph2f8EgmHSodz3ttgSf2DHzi/Ez1xUkk7NOlgNtmsxEdrM47+cC5ae\n"
+ "fIf2V+1o1JW8J7D11RmRbNPh3vfisueB4f88\n"
+ "-----END CERTIFICATE-----\n";
+
+static const std::string kTestRsa2048ClientPrivKey =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCN2hF4dUuRU23r\n"
+ "eyVTjbMH30T2npHSCud3G/EpwG0jvV+TiwOMxsHZKwbw07Hfekiq6ScZqDThNQ4W\n"
+ "2YdOSj5VgD5BimnIxlms/ku24gzi9COmmi205VP4pdVG/aJT0gVVMdJ4XbRBYCDs\n"
+ "3XZz+v9SpsiCHkcUuZUynoOGr6lJGTV5M/8C9XRwRS+xTRX9EgnG6LbLm/3XhW42\n"
+ "duy0wzfkiAYBbbUzaguTU76AQKDHcgjgIDWMGhzvnyoit3nXyYnmsOxw/KqpkMXK\n"
+ "QrUZQPAlD4+xn0flh4giZpXr4Z5BVwqOekdQUtBeMLg+Cj3UlrfIHNu3ox//f89n\n"
+ "CqP+kiyTAgMBAAECggEAAa64eP6ggCob1P3c73oayYPIbvRqiQdAFOrr7Vwu7zbr\n"
+ "z0rde+n6RU0mrpc+4NuzyPMtrOGQiatLbidJB5Cx3z8U00ovqbCl7PtcgorOhFKe\n"
+ "VEzihebCcYyQqbWQcKtpDMhOgBxRwFoXieJb6VGXfa96FAZalCWvXgOrTl7/BF2X\n"
+ "qMqIm9nJi+yS5tIO8VdOsOmrMWRH/b/ENUcef4WpLoxTXr0EEgyKWraeZ/hhXo1e\n"
+ "z29dZKqdr9wMsq11NPsRddwS94jnDkXTo+EQyWVTfB7gb6yyp07s8jysaDb21tVv\n"
+ "UXB9MRhDV1mOv0ncXfXZ4/+4A2UahmZaLDAVLaat4QKBgQDAVRredhGRGl2Nkic3\n"
+ "KvZCAfyxug788CgasBdEiouz19iCCwcgMIDwnq0s3/WM7h/laCamT2x38riYDnpq\n"
+ "rkYMfuVtU9CjEL9pTrdfwbIRhTwYNqADaPz2mXwQUhRXutE5TIdgxxC/a+ZTh0qN\n"
+ "S+vhTj/4hf0IZhMh5Nqj7IPExQKBgQC8zxEzhmSGjys0GuE6Wl6Doo2TpiR6vwvi\n"
+ "xPLU9lmIz5eca/Rd/eERioFQqeoIWDLzx52DXuz6rUoQhbJWz9hP3yqCwXD+pbNP\n"
+ "oDJqDDbCC4IMYEb0IK/PEPH+gIpnTjoFcW+ecKDFG7W5Lt05J8WsJsfOaJvMrOU+\n"
+ "dLXq3IgxdwKBgQC5RAFq0v6e8G+3hFaEHL0z3igkpt3zJf7rnj37hx2FMmDa+3Z0\n"
+ "umQp5B9af61PgL12xLmeMBmC/Wp1BlVDV/Yf6Uhk5Hyv5t0KuomHEtTNbbLyfAPs\n"
+ "5P/vJu/L5NS1oT4S3LX3MineyjgGs+bLbpub3z1dzutrYLADUSiPCK/xJQKBgBQt\n"
+ "nQ0Ao+Wtj1R2OvPdjJRM3wyUiPmFSWPm4HzaBx+T8AQLlYYmB9O0FbXlMtnJc0iS\n"
+ "YMcVcgYoVu4FG9YjSF7g3s4yljzgwJUV7c1fmMqMKE3iTDLy+1cJ3JLycdgwiArk\n"
+ "4KTyLHxkRbuQwpvFIF8RlfD9RQlOwQE3v+llwDhpAoGBAL6XG6Rp6mBoD2Ds5c9R\n"
+ "943yYgSUes3ji1SI9zFqeJtj8Ml/enuK1xu+8E/BxB0//+vgZsH6i3i8GFwygKey\n"
+ "CGJF8CbiHc3EJc3NQIIRXcni/CGacf0HwC6m+PGFDBIpA4H2iDpVvCSofxttQiq0\n"
+ "/Z7HXmXUvZHVyYi/QzX2Gahj\n"
+ "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048UnknownPrivKey =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrIhr+CS+6UI0w\n"
+ "CTaVzQAicKBe6X531LeQAGYx7j5RLHR1QIoJ0WCc5msmXKe2VzcWuLbVdTGAIP1H\n"
+ "mwbPqlbO4ioxeJhiDv+WPuLG8+j4Iw1Yqxt8cfohxjfvNmIQM8aF5hGyyaaTetDF\n"
+ "EYWONoYCBC4WnFWgYCPb8mzWXlhHE3F66GnHpc32zydPTg3ZurGvSsFf7fNY9yRw\n"
+ "8WtwPiI6mpRxt+n2bQUp+LZ+g/3rXLFPg8uWDGYG7IvLluWc9gR9lxjL64t6ryLU\n"
+ "2cm7eTfDgLw/B1F/wEgCJDnby1JgQ4rq6klJO3BR2ooUr/7T343y5njG5hQJreV7\n"
+ "5ZnSmRLZAgMBAAECggEABPrfeHZFuWkj7KqN+DbAmt/2aMCodZ3+7/20+528WkIe\n"
+ "CvXzdmTth+9UHagLWNzpnVuHdYd9JuZ+3F00aelh8JAIDIu++naHhUSj9ohtRoBF\n"
+ "oIeNK5ZJAj/Zi5hkauaIz8dxyyc/VdIYfm2bundXd7pNqYqH2tyFWp6PwH67GKlZ\n"
+ "1lC7o8gKAK8sz9g0Ctdoe+hDqAsvYFCW4EWDM2qboucSgn8g3E/Gux/KrpXVv7d0\n"
+ "PMQ60m+dyTOCMGqXIoDR3TAvQR7ex5sQ/QZSREdxKy878s/2FY4ktxtCUWlhrmcI\n"
+ "VKtrDOGEKwNoiMluf2635rsVq2e01XhQlmdxbRFU0QKBgQDjOhhD1m9duFTQ2b+J\n"
+ "Xfn6m8Rs7sZqO4Az7gLOWmD/vYWlK4n2nZsh6u5/cB1N+PA+ncvvV4yKJAlLHxbT\n"
+ "pVvfzJ/jbUsj/NJg/w7+KYC9gXgRmBonuG2gRZF/5Otdlza4vMcoSkqGjlGxJyzL\n"
+ "+9umEziN3tEYMRwipYvt7BgbUQKBgQDAzaXryJ3YD3jpecy/+fSnQvFjpyeDRqU1\n"
+ "KDA9nxN5tJN6bnKhUlMhy64SsgvVX9jUuN7cK+qYV0uzdBn6kIAJNLWTdbtH93+e\n"
+ "vNVgluR3jmixW4QfY9vfZKdXZbVGNc0DFMi1vJqgxTgQ5Mq5PxxxRL4FsAF840V1\n"
+ "Wu9uhU0NCQKBgBfjga2QG8E0oeYbHmHouWE5gxsYt09v1fifqzfalJwOZsCIpUaC\n"
+ "J08Xjd9kABC0fT14BXqyL5pOU5PMPvAdUF1k++JDGUU9TTjZV9AsuNYziFYBMa6/\n"
+ "WvcgmT1i6cO7JAuj/SQlO1SOHdSME8+WOO9q0eVIaZ8repPB58YprhchAoGBAJyR\n"
+ "Y8AJdkTSq7nNszvi245IioYGY8vzPo3gSOyBlesrfOfbcTMYC3JSWNXNyFZKM2br\n"
+ "ie75qtRzb4IXMlGLrq3LI/jPjnpuvjBF4HFDl9yOxO3iB3UGPrM2pb4PVhnh7s4l\n"
+ "vqf2tQsBnPn7EbVFTu+ch0NPHqYwWWNnqS/zCBMhAoGBAIkYjOE0iD9W2FXee6VL\n"
+ "iN8wDqlqsGEEtLvykIDmTmM+ZX5ftQuPo18khpE9wQKmJ5OpoVTYIP1UsJFBakgo\n"
+ "+dGaf6xVuPvmydNFqixlW3z227n4Px6GX7CXlCaAleTeItezli+dWf/9astwTA3x\n"
+ "IazYzsxUUpZFC4dJ1GhBn3y1\n"
+ "-----END PRIVATE KEY-----\n";
+
+static const std::string kTestRsa2048UnknownCert =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
+ "MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyNDE4MzMwNVoX\n"
+ "DTMwMDEyMTE4MzMwNVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
+ "DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKsi\n"
+ "Gv4JL7pQjTAJNpXNACJwoF7pfnfUt5AAZjHuPlEsdHVAignRYJzmayZcp7ZXNxa4\n"
+ "ttV1MYAg/UebBs+qVs7iKjF4mGIO/5Y+4sbz6PgjDVirG3xx+iHGN+82YhAzxoXm\n"
+ "EbLJppN60MURhY42hgIELhacVaBgI9vybNZeWEcTcXroacelzfbPJ09ODdm6sa9K\n"
+ "wV/t81j3JHDxa3A+IjqalHG36fZtBSn4tn6D/etcsU+Dy5YMZgbsi8uW5Zz2BH2X\n"
+ "GMvri3qvItTZybt5N8OAvD8HUX/ASAIkOdvLUmBDiurqSUk7cFHaihSv/tPfjfLm\n"
+ "eMbmFAmt5XvlmdKZEtkCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
+ "Af8EBAMCAYYwHQYDVR0OBBYEFDtRSOm1ilhnq6bKN4qJ1ekK/PAkMA0GCSqGSIb3\n"
+ "DQEBCwUAA4IBAQAP6Q8/OxnBA3BO8oxKer0tjI4rZMefUhbAKUWXYjTTNEBm5//b\n"
+ "lVGP2RptO7bxj8w1L3rxsjmVcv2TqBOhrbJqvGVPE2ntoYlFhBBkRvmxuu1y5W9V\n"
+ "uJU7SF9lNmDXShTURULu3P8GdeT1HGeXzWQ4x7VhY9a3VIbmN5VxjB+3C6hYZxSs\n"
+ "DCpmidu/sR+n5Azlh6oqrhOxmv17PuF/ioTUsHd4y2Z41IvvO47oghxNDtboUUsg\n"
+ "LfsM1MOxVC9PqOfQphFU4i8owNIYzBMadDLw+1TSQj0ALqZVyc9Dq+WDFdz+JAE+\n"
+ "k7TkVU06UPGVSnLVzJeYwGCXQp3apBszY9vO\n"
+ "-----END CERTIFICATE-----\n";
+
+struct CAIssuerField {
+ int nid;
+ std::vector<uint8_t> val;
+};
+using CAIssuer = std::vector<CAIssuerField>;
+static std::vector<CAIssuer> kCAIssuers = {
+ {
+ {NID_commonName, {'a', 'b', 'c', 'd', 'e'}},
+ {NID_organizationName, {'d', 'e', 'f', 'g'}},
+ },
+ {
+ {NID_commonName, {'h', 'i', 'j', 'k', 'l', 'm'}},
+ {NID_countryName, {'n', 'o'}},
+ },
+};
+
+class AdbWifiTlsConnectionTest : public testing::Test {
+ protected:
+ virtual void SetUp() override {
+ android::base::Socketpair(SOCK_STREAM, &server_fd_, &client_fd_);
+ server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert,
+ kTestRsa2048ServerPrivKey, server_fd_);
+ client_ = TlsConnection::Create(TlsConnection::Role::Client, kTestRsa2048ClientCert,
+ kTestRsa2048ClientPrivKey, client_fd_);
+ ASSERT_NE(nullptr, server_);
+ ASSERT_NE(nullptr, client_);
+ }
+
+ virtual void TearDown() override {
+ WaitForClientConnection();
+ // Shutdown the SSL connection first.
+ server_.reset();
+ client_.reset();
+ }
+
+ bssl::UniquePtr<STACK_OF(X509_NAME)> GetCAIssuerList() {
+ bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
+ for (auto& issuer : kCAIssuers) {
+ bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
+ for (auto& attr : issuer) {
+ CHECK(X509_NAME_add_entry_by_NID(name.get(), attr.nid, MBSTRING_ASC,
+ attr.val.data(), attr.val.size(), -1, 0));
+ }
+
+ CHECK(bssl::PushToStack(ret.get(), std::move(name)));
+ }
+
+ return ret;
+ }
+
+ void StartClientHandshakeAsync(TlsError expected) {
+ client_thread_ = std::thread([=]() { EXPECT_EQ(client_->DoHandshake(), expected); });
+ }
+
+ void WaitForClientConnection() {
+ if (client_thread_.joinable()) {
+ client_thread_.join();
+ }
+ }
+
+ unique_fd server_fd_;
+ unique_fd client_fd_;
+ const std::vector<uint8_t> msg_{0xff, 0xab, 0x32, 0xf6, 0x12, 0x56};
+ std::unique_ptr<TlsConnection> server_;
+ std::unique_ptr<TlsConnection> client_;
+ std::thread client_thread_;
+};
+
+TEST_F(AdbWifiTlsConnectionTest, InvalidCreationParams) {
+ // Verify that passing empty certificate/private key results in a crash.
+ ASSERT_DEATH(
+ {
+ server_ = TlsConnection::Create(TlsConnection::Role::Server, "",
+ kTestRsa2048ServerPrivKey, server_fd_);
+ },
+ "");
+ ASSERT_DEATH(
+ {
+ server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert,
+ "", server_fd_);
+ },
+ "");
+ ASSERT_DEATH(
+ {
+ client_ = TlsConnection::Create(TlsConnection::Role::Client, "",
+ kTestRsa2048ClientPrivKey, client_fd_);
+ },
+ "");
+ ASSERT_DEATH(
+ {
+ client_ = TlsConnection::Create(TlsConnection::Role::Client, kTestRsa2048ClientCert,
+ "", client_fd_);
+ },
+ "");
+}
+
+TEST_F(AdbWifiTlsConnectionTest, NoCertificateVerification) {
+ // Allow any certificate
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ StartClientHandshakeAsync(TlsError::Success);
+
+ // Handshake should succeed
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+ WaitForClientConnection();
+
+ // Test client/server read and writes
+ client_thread_ = std::thread([&]() {
+ EXPECT_TRUE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // Try with overloaded ReadFully
+ std::vector<uint8_t> buf(msg_.size());
+ ASSERT_TRUE(client_->ReadFully(buf.data(), msg_.size()));
+ EXPECT_EQ(buf, msg_);
+ });
+
+ auto data = server_->ReadFully(msg_.size());
+ EXPECT_EQ(data, msg_);
+ EXPECT_TRUE(server_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, NoTrustedCertificates) {
+ StartClientHandshakeAsync(TlsError::CertificateRejected);
+
+ // Handshake should not succeed
+ ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+ WaitForClientConnection();
+
+ // All writes and reads should fail
+ client_thread_ = std::thread([&]() {
+ // Client write, server read should fail
+ EXPECT_FALSE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ });
+
+ auto data = server_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ EXPECT_FALSE(server_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates) {
+ // Add peer certificates
+ EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
+ EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
+
+ StartClientHandshakeAsync(TlsError::Success);
+
+ // Handshake should succeed
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+ WaitForClientConnection();
+
+ // All read writes should succeed
+ client_thread_ = std::thread([&]() {
+ EXPECT_TRUE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data, msg_);
+ });
+
+ auto data = server_->ReadFully(msg_.size());
+ EXPECT_EQ(data, msg_);
+ EXPECT_TRUE(server_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates_ClientWrongCert) {
+ // Server trusts a certificate, client has the wrong certificate
+ EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
+ // Client accepts any certificate
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+ // Without enabling EnableClientPostHandshakeCheck(), DoHandshake() will
+ // succeed, because in TLS 1.3, the client doesn't get notified if the
+ // server rejected the certificate until a read operation is called.
+ StartClientHandshakeAsync(TlsError::Success);
+
+ // Handshake should fail for server, succeed for client
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+ WaitForClientConnection();
+
+ // Client writes will succeed, everything else will fail.
+ client_thread_ = std::thread([&]() {
+ EXPECT_TRUE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ });
+
+ auto data = server_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ EXPECT_FALSE(server_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, ExportKeyingMaterial) {
+ // Allow any certificate
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+ // Add peer certificates
+ EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
+ EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
+
+ StartClientHandshakeAsync(TlsError::Success);
+
+ // Handshake should succeed
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+ WaitForClientConnection();
+
+ // Verify the client and server's exported key material match.
+ const size_t key_size = 64;
+ auto client_key_material = client_->ExportKeyingMaterial(key_size);
+ ASSERT_FALSE(client_key_material.empty());
+ auto server_key_material = server_->ExportKeyingMaterial(key_size);
+ ASSERT_TRUE(!server_key_material.empty());
+ ASSERT_EQ(client_key_material.size(), key_size);
+ ASSERT_EQ(client_key_material, server_key_material);
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientAcceptsServerRejects) {
+ // Client accepts all
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ // Server rejects all
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+ // Client handshake should succeed, because in TLS 1.3, client does not
+ // realize that the peer rejected the certificate until after a read
+ // operation.
+ StartClientHandshakeAsync(TlsError::Success);
+
+ // Server handshake should fail
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientAcceptsServerRejects_PostHSCheck) {
+ // Client accepts all
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ // Client should now get a failure in the handshake
+ client_->EnableClientPostHandshakeCheck(true);
+ // Server rejects all
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+
+ // Client handshake should fail because server rejects everything
+ StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
+
+ // Server handshake should fail
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientRejectsServerAccepts) {
+ // Client rejects all
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+ // Server accepts all
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ // Client handshake should fail
+ StartClientHandshakeAsync(TlsError::CertificateRejected);
+
+ // Server handshake should fail
+ ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetCertVerifyCallback_ClientRejectsServerAccepts_PostHSCheck) {
+ // Client rejects all
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
+ // This shouldn't affect the error types returned in the
+ // #SetCertVerifyCallback_ClientRejectsServerAccepts test, since
+ // the failure is still within the TLS 1.3 handshake.
+ client_->EnableClientPostHandshakeCheck(true);
+ // Server accepts all
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+ // Client handshake should fail
+ StartClientHandshakeAsync(TlsError::CertificateRejected);
+
+ // Server handshake should fail
+ ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, EnableClientPostHandshakeCheck_ClientWrongCert) {
+ client_->AddTrustedCertificate(kTestRsa2048ServerCert);
+ // client's DoHandshake() will fail if the server rejected the certificate
+ client_->EnableClientPostHandshakeCheck(true);
+
+ // Add peer certificates
+ EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
+
+ // Handshake should fail for client
+ StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
+
+ // Handshake should fail for server
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+ WaitForClientConnection();
+
+ // All read writes should fail
+ client_thread_ = std::thread([&]() {
+ EXPECT_FALSE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ });
+
+ auto data = server_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ EXPECT_FALSE(server_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+
+ WaitForClientConnection();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Empty) {
+ // Setting an empty CA list should not crash
+ server_->SetClientCAList(nullptr);
+ ASSERT_DEATH(
+ {
+ // Client cannot use this API
+ client_->SetClientCAList(nullptr);
+ },
+ "");
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Smoke) {
+ auto bsslIssuerList = GetCAIssuerList();
+ server_->SetClientCAList(bsslIssuerList.get());
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+ client_thread_ = std::thread([&]() {
+ client_->SetCertificateCallback([&](SSL* ssl) -> int {
+ const STACK_OF(X509_NAME)* received = SSL_get_client_CA_list(ssl);
+ EXPECT_NE(received, nullptr);
+ const size_t num_names = sk_X509_NAME_num(received);
+ EXPECT_EQ(kCAIssuers.size(), num_names);
+
+ // Client initially registered with the wrong key. Let's change it
+ // here to verify this callback actually changes the client
+ // certificate to the right one.
+ EXPECT_TRUE(TlsConnection::SetCertAndKey(ssl, kTestRsa2048UnknownCert,
+ kTestRsa2048UnknownPrivKey));
+
+ const size_t buf_size = 256;
+ uint8_t buf[buf_size];
+ size_t idx = 0;
+ for (auto& issuer : kCAIssuers) {
+ auto* name = sk_X509_NAME_value(received, idx++);
+ for (auto& attr : issuer) {
+ EXPECT_EQ(X509_NAME_get_text_by_NID(name, attr.nid,
+ reinterpret_cast<char*>(buf), buf_size),
+ attr.val.size());
+ std::vector<uint8_t> out(buf, buf + attr.val.size());
+ EXPECT_EQ(out, attr.val);
+ }
+ }
+
+ return 1;
+ });
+ // Client handshake should succeed
+ ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
+ });
+
+ EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
+ // Server handshake should succeed
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+ client_thread_.join();
+}
+
+TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_AdbCAList) {
+ bssl::UniquePtr<STACK_OF(X509_NAME)> ca_list(sk_X509_NAME_new_null());
+ std::string keyhash = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ auto issuer = CreateCAIssuerFromEncodedKey(keyhash);
+ ASSERT_TRUE(bssl::PushToStack(ca_list.get(), std::move(issuer)));
+ server_->SetClientCAList(ca_list.get());
+ client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+
+ client_thread_ = std::thread([&]() {
+ client_->SetCertificateCallback([&](SSL* ssl) -> int {
+ // Client initially registered with a certificate that is not trusted by
+ // the server. Let's test that we can change the certificate to the
+ // trusted one here.
+ const STACK_OF(X509_NAME)* received = SSL_get_client_CA_list(ssl);
+ EXPECT_NE(received, nullptr);
+ const size_t num_names = sk_X509_NAME_num(received);
+ EXPECT_EQ(1, num_names);
+
+ auto* name = sk_X509_NAME_value(received, 0);
+ EXPECT_NE(name, nullptr);
+ auto enc_key = ParseEncodedKeyFromCAIssuer(name);
+ EXPECT_EQ(keyhash, enc_key);
+
+ return 1;
+ });
+ // Client handshake should succeed
+ ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
+ });
+
+ server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
+ // Server handshake should succeed
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
+ client_thread_.join();
+}
+} // namespace tls
+} // namespace adb
diff --git a/adb/tls/tls_connection.cpp b/adb/tls/tls_connection.cpp
new file mode 100644
index 0000000..853cdac
--- /dev/null
+++ b/adb/tls/tls_connection.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "adb/tls/tls_connection.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+using android::base::borrowed_fd;
+
+namespace adb {
+namespace tls {
+
+namespace {
+
+static constexpr char kExportedKeyLabel[] = "adb-label";
+
+class TlsConnectionImpl : public TlsConnection {
+ public:
+ explicit TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key,
+ borrowed_fd fd);
+ ~TlsConnectionImpl() override;
+
+ bool AddTrustedCertificate(std::string_view cert) override;
+ void SetCertVerifyCallback(CertVerifyCb cb) override;
+ void SetCertificateCallback(SetCertCb cb) override;
+ void SetClientCAList(STACK_OF(X509_NAME) * ca_list) override;
+ std::vector<uint8_t> ExportKeyingMaterial(size_t length) override;
+ void EnableClientPostHandshakeCheck(bool enable) override;
+ TlsError DoHandshake() override;
+ std::vector<uint8_t> ReadFully(size_t size) override;
+ bool ReadFully(void* buf, size_t size) override;
+ bool WriteFully(std::string_view data) override;
+
+ static bssl::UniquePtr<EVP_PKEY> EvpPkeyFromPEM(std::string_view pem);
+ static bssl::UniquePtr<CRYPTO_BUFFER> BufferFromPEM(std::string_view pem);
+
+ private:
+ static int SSLSetCertVerifyCb(X509_STORE_CTX* ctx, void* opaque);
+ static int SSLSetCertCb(SSL* ssl, void* opaque);
+
+ static bssl::UniquePtr<X509> X509FromBuffer(bssl::UniquePtr<CRYPTO_BUFFER> buffer);
+ static const char* SSLErrorString();
+ void Invalidate();
+ TlsError GetFailureReason(int err);
+ const char* RoleToString() { return role_ == Role::Server ? kServerRoleStr : kClientRoleStr; }
+
+ Role role_;
+ bssl::UniquePtr<EVP_PKEY> priv_key_;
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_;
+
+ bssl::UniquePtr<STACK_OF(X509_NAME)> ca_list_;
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+ bssl::UniquePtr<SSL> ssl_;
+ std::vector<bssl::UniquePtr<X509>> known_certificates_;
+ bool client_verify_post_handshake_ = false;
+
+ CertVerifyCb cert_verify_cb_;
+ SetCertCb set_cert_cb_;
+ borrowed_fd fd_;
+ static constexpr char kClientRoleStr[] = "[client]: ";
+ static constexpr char kServerRoleStr[] = "[server]: ";
+}; // TlsConnectionImpl
+
+TlsConnectionImpl::TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key,
+ borrowed_fd fd)
+ : role_(role), fd_(fd) {
+ CHECK(!cert.empty() && !priv_key.empty());
+ LOG(INFO) << RoleToString() << "Initializing adbwifi TlsConnection";
+ cert_ = BufferFromPEM(cert);
+ CHECK(cert_);
+ priv_key_ = EvpPkeyFromPEM(priv_key);
+ CHECK(priv_key_);
+}
+
+TlsConnectionImpl::~TlsConnectionImpl() {
+ // shutdown the SSL connection
+ if (ssl_ != nullptr) {
+ SSL_shutdown(ssl_.get());
+ }
+}
+
+// static
+const char* TlsConnectionImpl::SSLErrorString() {
+ auto sslerr = ERR_peek_last_error();
+ return ERR_reason_error_string(sslerr);
+}
+
+// static
+bssl::UniquePtr<EVP_PKEY> TlsConnectionImpl::EvpPkeyFromPEM(std::string_view pem) {
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+ return bssl::UniquePtr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+}
+
+// static
+bssl::UniquePtr<CRYPTO_BUFFER> TlsConnectionImpl::BufferFromPEM(std::string_view pem) {
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+ char* name = nullptr;
+ char* header = nullptr;
+ uint8_t* data = nullptr;
+ long data_len = 0;
+
+ if (!PEM_read_bio(bio.get(), &name, &header, &data, &data_len)) {
+ LOG(ERROR) << "Failed to read certificate";
+ return nullptr;
+ }
+ OPENSSL_free(name);
+ OPENSSL_free(header);
+
+ auto ret = bssl::UniquePtr<CRYPTO_BUFFER>(CRYPTO_BUFFER_new(data, data_len, nullptr));
+ OPENSSL_free(data);
+ return ret;
+}
+
+// static
+bssl::UniquePtr<X509> TlsConnectionImpl::X509FromBuffer(bssl::UniquePtr<CRYPTO_BUFFER> buffer) {
+ if (!buffer) {
+ return nullptr;
+ }
+ return bssl::UniquePtr<X509>(X509_parse_from_buffer(buffer.get()));
+}
+
+// static
+int TlsConnectionImpl::SSLSetCertVerifyCb(X509_STORE_CTX* ctx, void* opaque) {
+ auto* p = reinterpret_cast<TlsConnectionImpl*>(opaque);
+ return p->cert_verify_cb_(ctx);
+}
+
+// static
+int TlsConnectionImpl::SSLSetCertCb(SSL* ssl, void* opaque) {
+ auto* p = reinterpret_cast<TlsConnectionImpl*>(opaque);
+ return p->set_cert_cb_(ssl);
+}
+
+bool TlsConnectionImpl::AddTrustedCertificate(std::string_view cert) {
+ // Create X509 buffer from the certificate string
+ auto buf = X509FromBuffer(BufferFromPEM(cert));
+ if (buf == nullptr) {
+ LOG(ERROR) << RoleToString() << "Failed to create a X509 buffer for the certificate.";
+ return false;
+ }
+ known_certificates_.push_back(std::move(buf));
+ return true;
+}
+
+void TlsConnectionImpl::SetCertVerifyCallback(CertVerifyCb cb) {
+ cert_verify_cb_ = cb;
+}
+
+void TlsConnectionImpl::SetCertificateCallback(SetCertCb cb) {
+ set_cert_cb_ = cb;
+}
+
+void TlsConnectionImpl::SetClientCAList(STACK_OF(X509_NAME) * ca_list) {
+ CHECK(role_ == Role::Server);
+ ca_list_.reset(ca_list != nullptr ? SSL_dup_CA_list(ca_list) : nullptr);
+}
+
+std::vector<uint8_t> TlsConnectionImpl::ExportKeyingMaterial(size_t length) {
+ if (ssl_.get() == nullptr) {
+ return {};
+ }
+
+ std::vector<uint8_t> out(length);
+ if (SSL_export_keying_material(ssl_.get(), out.data(), out.size(), kExportedKeyLabel,
+ sizeof(kExportedKeyLabel), nullptr, 0, false) == 0) {
+ return {};
+ }
+ return out;
+}
+
+void TlsConnectionImpl::EnableClientPostHandshakeCheck(bool enable) {
+ client_verify_post_handshake_ = enable;
+}
+
+TlsConnection::TlsError TlsConnectionImpl::GetFailureReason(int err) {
+ switch (ERR_GET_REASON(err)) {
+ case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
+ case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
+ case SSL_R_TLSV1_ALERT_ACCESS_DENIED:
+ case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
+ case SSL_R_TLSV1_CERTIFICATE_REQUIRED:
+ return TlsError::PeerRejectedCertificate;
+ case SSL_R_CERTIFICATE_VERIFY_FAILED:
+ return TlsError::CertificateRejected;
+ default:
+ return TlsError::UnknownFailure;
+ }
+}
+
+TlsConnection::TlsError TlsConnectionImpl::DoHandshake() {
+ LOG(INFO) << RoleToString() << "Starting adbwifi tls handshake";
+ ssl_ctx_.reset(SSL_CTX_new(TLS_method()));
+ // TODO: Remove set_max_proto_version() once external/boringssl is updated
+ // past
+ // https://boringssl.googlesource.com/boringssl/+/58d56f4c59969a23e5f52014e2651c76fea2f877
+ if (ssl_ctx_.get() == nullptr ||
+ !SSL_CTX_set_min_proto_version(ssl_ctx_.get(), TLS1_3_VERSION) ||
+ !SSL_CTX_set_max_proto_version(ssl_ctx_.get(), TLS1_3_VERSION)) {
+ LOG(ERROR) << RoleToString() << "Failed to create SSL context";
+ return TlsError::UnknownFailure;
+ }
+
+ // Register user-supplied known certificates
+ for (auto const& cert : known_certificates_) {
+ if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx_.get()), cert.get()) == 0) {
+ LOG(ERROR) << RoleToString() << "Unable to add certificates into the X509_STORE";
+ return TlsError::UnknownFailure;
+ }
+ }
+
+ // Custom certificate verification
+ if (cert_verify_cb_) {
+ SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), SSLSetCertVerifyCb, this);
+ }
+
+ // set select certificate callback, if any.
+ if (set_cert_cb_) {
+ SSL_CTX_set_cert_cb(ssl_ctx_.get(), SSLSetCertCb, this);
+ }
+
+ // Server-allowed client CA list
+ if (ca_list_ != nullptr) {
+ bssl::UniquePtr<STACK_OF(X509_NAME)> names(SSL_dup_CA_list(ca_list_.get()));
+ SSL_CTX_set_client_CA_list(ssl_ctx_.get(), names.release());
+ }
+
+ // Register our certificate and private key.
+ std::vector<CRYPTO_BUFFER*> cert_chain = {
+ cert_.get(),
+ };
+ if (!SSL_CTX_set_chain_and_key(ssl_ctx_.get(), cert_chain.data(), cert_chain.size(),
+ priv_key_.get(), nullptr)) {
+ LOG(ERROR) << RoleToString()
+ << "Unable to register the certificate chain file and private key ["
+ << SSLErrorString() << "]";
+ Invalidate();
+ return TlsError::UnknownFailure;
+ }
+
+ SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+
+ // Okay! Let's try to do the handshake!
+ ssl_.reset(SSL_new(ssl_ctx_.get()));
+ if (!SSL_set_fd(ssl_.get(), fd_.get())) {
+ LOG(ERROR) << RoleToString() << "SSL_set_fd failed. [" << SSLErrorString() << "]";
+ return TlsError::UnknownFailure;
+ }
+
+ switch (role_) {
+ case Role::Server:
+ SSL_set_accept_state(ssl_.get());
+ break;
+ case Role::Client:
+ SSL_set_connect_state(ssl_.get());
+ break;
+ }
+ if (SSL_do_handshake(ssl_.get()) != 1) {
+ LOG(ERROR) << RoleToString() << "Handshake failed in SSL_accept/SSL_connect ["
+ << SSLErrorString() << "]";
+ auto sslerr = ERR_get_error();
+ Invalidate();
+ return GetFailureReason(sslerr);
+ }
+
+ if (client_verify_post_handshake_ && role_ == Role::Client) {
+ uint8_t check;
+ // Try to peek one byte for any failures. This assumes on success that
+ // the server actually sends something.
+ if (SSL_peek(ssl_.get(), &check, 1) <= 0) {
+ LOG(ERROR) << RoleToString() << "Post-handshake SSL_peek failed [" << SSLErrorString()
+ << "]";
+ auto sslerr = ERR_get_error();
+ Invalidate();
+ return GetFailureReason(sslerr);
+ }
+ }
+
+ LOG(INFO) << RoleToString() << "Handshake succeeded.";
+ return TlsError::Success;
+}
+
+void TlsConnectionImpl::Invalidate() {
+ ssl_.reset();
+ ssl_ctx_.reset();
+}
+
+std::vector<uint8_t> TlsConnectionImpl::ReadFully(size_t size) {
+ std::vector<uint8_t> buf(size);
+ if (!ReadFully(buf.data(), buf.size())) {
+ return {};
+ }
+
+ return buf;
+}
+
+bool TlsConnectionImpl::ReadFully(void* buf, size_t size) {
+ CHECK_GT(size, 0U);
+ if (!ssl_) {
+ LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
+ return false;
+ }
+
+ size_t offset = 0;
+ uint8_t* p8 = reinterpret_cast<uint8_t*>(buf);
+ while (size > 0) {
+ int bytes_read =
+ SSL_read(ssl_.get(), p8 + offset, std::min(static_cast<size_t>(INT_MAX), size));
+ if (bytes_read <= 0) {
+ LOG(ERROR) << RoleToString() << "SSL_read failed [" << SSLErrorString() << "]";
+ return false;
+ }
+ size -= bytes_read;
+ offset += bytes_read;
+ }
+ return true;
+}
+
+bool TlsConnectionImpl::WriteFully(std::string_view data) {
+ CHECK(!data.empty());
+ if (!ssl_) {
+ LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
+ return false;
+ }
+
+ while (!data.empty()) {
+ int bytes_out = SSL_write(ssl_.get(), data.data(),
+ std::min(static_cast<size_t>(INT_MAX), data.size()));
+ if (bytes_out <= 0) {
+ LOG(ERROR) << RoleToString() << "SSL_write failed [" << SSLErrorString() << "]";
+ return false;
+ }
+ data = data.substr(bytes_out);
+ }
+ return true;
+}
+} // namespace
+
+// static
+std::unique_ptr<TlsConnection> TlsConnection::Create(TlsConnection::Role role,
+ std::string_view cert,
+ std::string_view priv_key, borrowed_fd fd) {
+ CHECK(!cert.empty());
+ CHECK(!priv_key.empty());
+
+ return std::make_unique<TlsConnectionImpl>(role, cert, priv_key, fd);
+}
+
+// static
+bool TlsConnection::SetCertAndKey(SSL* ssl, std::string_view cert, std::string_view priv_key) {
+ CHECK(ssl);
+ // Note: declaring these in local scope is okay because
+ // SSL_set_chain_and_key will increase the refcount (bssl::UpRef).
+ auto x509_cert = TlsConnectionImpl::BufferFromPEM(cert);
+ auto evp_pkey = TlsConnectionImpl::EvpPkeyFromPEM(priv_key);
+ if (x509_cert == nullptr || evp_pkey == nullptr) {
+ return false;
+ }
+
+ std::vector<CRYPTO_BUFFER*> cert_chain = {
+ x509_cert.get(),
+ };
+ if (!SSL_set_chain_and_key(ssl, cert_chain.data(), cert_chain.size(), evp_pkey.get(),
+ nullptr)) {
+ LOG(ERROR) << "SSL_set_chain_and_key failed";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace tls
+} // namespace adb
diff --git a/base/Android.bp b/base/Android.bp
index a32959b..25c74f2 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -128,6 +128,10 @@
static_libs: ["fmtlib"],
whole_static_libs: ["fmtlib"],
export_static_lib_headers: ["fmtlib"],
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
}
cc_library_static {
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 4a59552..5e65876 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -191,11 +191,6 @@
return ErrorCode(code, args...);
}
-// TODO(tomcherry): Remove this once we've removed all `using android::base::Errorf` and `using
-// android::base::ErrnoErrorf` lines.
-enum Errorf {};
-enum ErrnoErrorf {};
-
template <typename T, typename... Args>
inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 9ffe5dd..a9c1676 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -870,6 +870,7 @@
const char system_reboot_reason_property[] = "sys.boot.reason";
const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_reboot_reason_file[] = LAST_REBOOT_REASON_FILE;
const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
constexpr size_t history_reboot_reason_size = 4;
const char history_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY ".history";
@@ -1059,7 +1060,9 @@
if (isBluntRebootReason(ret)) {
// Content buffer no longer will have console data. Beware if more
// checks added below, that depend on parsing console content.
- content = android::base::GetProperty(last_reboot_reason_property, "");
+ if (!android::base::ReadFileToString(last_reboot_reason_file, &content)) {
+ content = android::base::GetProperty(last_reboot_reason_property, "");
+ }
transformReason(content);
// Anything in last is better than 'super-blunt' reboot or shutdown.
@@ -1233,7 +1236,10 @@
// Record the scrubbed system_boot_reason to the property
BootReasonAddToHistory(system_boot_reason);
// Shift last_reboot_reason_property to last_last_reboot_reason_property
- auto last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
+ std::string last_boot_reason;
+ if (!android::base::ReadFileToString(last_reboot_reason_file, &last_boot_reason)) {
+ last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
+ }
if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
last_boot_reason = system_boot_reason;
} else {
@@ -1241,6 +1247,7 @@
}
android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);
android::base::SetProperty(last_reboot_reason_property, "");
+ unlink(last_reboot_reason_file);
}
// Gets the boot time offset. This is useful when Android is running in a
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index e3ce531..f28c778 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -171,6 +171,7 @@
srcs: [
"libdebuggerd/backtrace.cpp",
+ "libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
@@ -181,7 +182,10 @@
// Needed for private/bionic_fdsan.h
include_dirs: ["bionic/libc"],
- header_libs: ["bionic_libc_platform_headers"],
+ header_libs: [
+ "bionic_libc_platform_headers",
+ "gwp_asan_headers",
+ ],
static_libs: [
"libdexfile_support_static", // libunwindstack dependency
@@ -192,6 +196,8 @@
"liblog",
],
+ whole_static_libs: ["gwp_asan_crash_handler"],
+
target: {
recovery: {
exclude_static_libs: [
@@ -246,10 +252,12 @@
static_libs: [
"libdebuggerd",
+ "libgmock",
],
header_libs: [
"bionic_libc_platform_headers",
+ "gwp_asan_headers",
],
local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 2545cd6..ebb8d86 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -73,8 +73,8 @@
unique_fd pipe_read, pipe_write;
ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
- // 64 MiB should be enough for everyone.
- constexpr int PIPE_SIZE = 64 * 1024 * 1024;
+ // 16 MiB should be enough for everyone.
+ constexpr int PIPE_SIZE = 16 * 1024 * 1024;
ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
// Wait for a bit to let the child spawn all of its threads.
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index e8f366f..3e99880 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -255,7 +255,8 @@
static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
- uintptr_t* fdsan_table_address) {
+ uintptr_t* fdsan_table_address, uintptr_t* gwp_asan_state,
+ uintptr_t* gwp_asan_metadata) {
std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
@@ -272,6 +273,10 @@
expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
break;
+ case 3:
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
+ break;
+
default:
LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
break;
@@ -284,7 +289,13 @@
}
*fdsan_table_address = 0;
+ *gwp_asan_state = 0;
+ *gwp_asan_metadata = 0;
switch (crash_info->header.version) {
+ case 3:
+ *gwp_asan_state = crash_info->data.v3.gwp_asan_state;
+ *gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata;
+ FALLTHROUGH_INTENDED;
case 2:
*fdsan_table_address = crash_info->data.v2.fdsan_table_address;
FALLTHROUGH_INTENDED;
@@ -416,6 +427,8 @@
DebuggerdDumpType dump_type;
uintptr_t abort_msg_address = 0;
uintptr_t fdsan_table_address = 0;
+ uintptr_t gwp_asan_state = 0;
+ uintptr_t gwp_asan_metadata = 0;
Initialize(argv);
ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -477,7 +490,7 @@
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
- &fdsan_table_address);
+ &fdsan_table_address, &gwp_asan_state, &gwp_asan_metadata);
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
} else {
@@ -592,7 +605,8 @@
{
ATRACE_NAME("engrave_tombstone");
engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
- abort_msg_address, &open_files, &amfd_data);
+ abort_msg_address, &open_files, &amfd_data, gwp_asan_state,
+ gwp_asan_metadata);
}
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index f8192b5..8b4b630 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -49,6 +49,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/macros.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
@@ -298,6 +299,8 @@
void* ucontext;
uintptr_t abort_msg;
uintptr_t fdsan_table;
+ uintptr_t gwp_asan_state;
+ uintptr_t gwp_asan_metadata;
};
// Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -342,23 +345,25 @@
}
// ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
- uint32_t version = 2;
- constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+ uint32_t version = 3;
+ constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
errno = 0;
if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
fatal_errno("failed to set pipe buffer size");
}
- struct iovec iovs[5] = {
+ struct iovec iovs[] = {
{.iov_base = &version, .iov_len = sizeof(version)},
{.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
{.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
{.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
{.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
};
- ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
+ ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
if (rc == -1) {
fatal_errno("failed to write crash info");
} else if (rc != expected) {
@@ -485,6 +490,8 @@
}
void* abort_message = nullptr;
+ const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
+ const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
@@ -499,6 +506,12 @@
if (g_callbacks.get_abort_message) {
abort_message = g_callbacks.get_abort_message();
}
+ if (g_callbacks.get_gwp_asan_state) {
+ gwp_asan_state = g_callbacks.get_gwp_asan_state();
+ }
+ if (g_callbacks.get_gwp_asan_metadata) {
+ gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
+ }
}
// If sival_int is ~0, it means that the fallback handler has been called
@@ -532,6 +545,8 @@
.ucontext = context,
.abort_msg = reinterpret_cast<uintptr_t>(abort_message),
.fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
+ .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
+ .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
};
// Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index cd6fc05..4f24360 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -24,11 +24,20 @@
__BEGIN_DECLS
+// Forward declare these classes so not everyone has to include GWP-ASan
+// headers.
+namespace gwp_asan {
+struct AllocatorState;
+struct AllocationMetadata;
+}; // namespace gwp_asan
+
// These callbacks are called in a signal handler, and thus must be async signal safe.
// If null, the callbacks will not be called.
typedef struct {
struct abort_msg_t* (*get_abort_message)();
void (*post_dump)();
+ const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)();
+ const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)();
} debuggerd_callbacks_t;
void debuggerd_init(debuggerd_callbacks_t* callbacks);
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
new file mode 100644
index 0000000..53df783
--- /dev/null
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/utility.h"
+
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+// Retrieve GWP-ASan state from `state_addr` inside the process at
+// `process_memory`. Place the state into `*state`.
+static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,
+ gwp_asan::AllocatorState* state) {
+ return process_memory->ReadFully(state_addr, state, sizeof(*state));
+}
+
+// Retrieve the GWP-ASan metadata pool from `metadata_addr` inside the process
+// at `process_memory`. The number of metadata slots is retrieved from the
+// allocator state provided. This function returns a heap-allocated copy of the
+// metadata pool whose ownership should be managed by the caller. Returns
+// nullptr on failure.
+static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
+ unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
+ uintptr_t metadata_addr) {
+ if (state.MaxSimultaneousAllocations > 1024) {
+ ALOGE(
+ "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
+ "exceeds maximum allowed (1024).",
+ state.MaxSimultaneousAllocations);
+ return nullptr;
+ }
+
+ gwp_asan::AllocationMetadata* meta =
+ new gwp_asan::AllocationMetadata[state.MaxSimultaneousAllocations];
+ if (!process_memory->ReadFully(metadata_addr, meta,
+ sizeof(*meta) * state.MaxSimultaneousAllocations)) {
+ ALOGE(
+ "Error when retrieving GWP-ASan metadata, could not retrieve %zu "
+ "pieces of metadata.",
+ state.MaxSimultaneousAllocations);
+ delete[] meta;
+ meta = nullptr;
+ }
+ return meta;
+}
+
+GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
+ uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
+ const ThreadInfo& thread_info) {
+ if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
+ // Extract the GWP-ASan regions from the dead process.
+ if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
+ metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
+ if (!metadata_.get()) return;
+
+ // Get the external crash address from the thread info.
+ crash_address_ = 0u;
+ if (signal_has_si_addr(thread_info.siginfo)) {
+ crash_address_ = reinterpret_cast<uintptr_t>(thread_info.siginfo->si_addr);
+ }
+
+ // Ensure the error belongs to GWP-ASan.
+ if (!__gwp_asan_error_is_mine(&state_, crash_address_)) return;
+
+ is_gwp_asan_responsible_ = true;
+ thread_id_ = thread_info.tid;
+
+ // Grab the internal error address, if it exists.
+ uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
+ if (internal_crash_address) {
+ crash_address_ = internal_crash_address;
+ }
+
+ // Get other information from the internal state.
+ error_ = __gwp_asan_diagnose_error(&state_, metadata_.get(), crash_address_);
+ error_string_ = gwp_asan::ErrorToString(error_);
+ responsible_allocation_ = __gwp_asan_get_metadata(&state_, metadata_.get(), crash_address_);
+}
+
+bool GwpAsanCrashData::CrashIsMine() const {
+ return is_gwp_asan_responsible_;
+}
+
+void GwpAsanCrashData::DumpCause(log_t* log) const {
+ if (!CrashIsMine()) {
+ ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
+ return;
+ }
+
+ if (error_ == gwp_asan::Error::UNKNOWN) {
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
+ crash_address_);
+ return;
+ }
+
+ if (!responsible_allocation_) {
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
+ crash_address_);
+ return;
+ }
+
+ uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
+ size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
+
+ if (crash_address_ == alloc_address) {
+ // Use After Free on a 41-byte allocation at 0xdeadbeef.
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_string_, alloc_size, alloc_address);
+ return;
+ }
+
+ uintptr_t diff;
+ const char* location_str;
+
+ if (crash_address_ < alloc_address) {
+ // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+ location_str = "left of";
+ diff = alloc_address - crash_address_;
+ } else if (crash_address_ - alloc_address < alloc_size) {
+ // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+ location_str = "into";
+ diff = crash_address_ - alloc_address;
+ } else {
+ // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
+ // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
+ location_str = "right of";
+ diff = crash_address_ - alloc_address;
+ if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
+ diff -= alloc_size;
+ }
+ }
+
+ // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+ const char* byte_suffix = "s";
+ if (diff == 1) {
+ byte_suffix = "";
+ }
+ _LOG(log, logtype::HEADER,
+ "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
+}
+
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
+ size_t frame_num) {
+ unwindstack::FrameData frame;
+ frame.num = frame_num;
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (!map_info) {
+ frame.rel_pc = pc;
+ return frame;
+ }
+
+ unwindstack::Elf* elf =
+ map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+ // Create registers just to get PC adjustment. Doesn't matter what they point
+ // to.
+ unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+ uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+ relative_pc -= pc_adjustment;
+ // The debug PC may be different if the PC comes from the JIT.
+ uint64_t debug_pc = relative_pc;
+
+ // If we don't have a valid ELF file, check the JIT.
+ if (!elf->valid()) {
+ unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+ uint64_t jit_pc = pc - pc_adjustment;
+ unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ if (jit_elf != nullptr) {
+ debug_pc = jit_pc;
+ elf = jit_elf;
+ }
+ }
+
+ // Copy all the things we need into the frame for symbolization.
+ frame.rel_pc = relative_pc;
+ frame.pc = pc - pc_adjustment;
+ frame.map_name = map_info->name;
+ frame.map_elf_start_offset = map_info->elf_start_offset;
+ frame.map_exact_offset = map_info->offset;
+ frame.map_start = map_info->start;
+ frame.map_end = map_info->end;
+ frame.map_flags = map_info->flags;
+ frame.map_load_bias = elf->GetLoadBias();
+
+ if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ frame.function_name = "";
+ frame.function_offset = 0;
+ }
+ return frame;
+}
+
+constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
+
+bool GwpAsanCrashData::HasDeallocationTrace() const {
+ assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
+ if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
+ return false;
+ }
+ return true;
+}
+
+void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+ assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
+ uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
+
+ std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+ size_t num_frames =
+ __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+ if (thread_id == gwp_asan::kInvalidThreadID) {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
+ } else {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < num_frames; ++i) {
+ unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+}
+
+bool GwpAsanCrashData::HasAllocationTrace() const {
+ assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
+ return responsible_allocation_ != nullptr;
+}
+
+void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+ assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
+ uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
+
+ std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+ size_t num_frames =
+ __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+ if (thread_id == gwp_asan::kInvalidThreadID) {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
+ } else {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < num_frames; ++i) {
+ unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
new file mode 100644
index 0000000..aef4c62
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <unwindstack/Memory.h>
+
+#include "gwp_asan/common.h"
+#include "types.h"
+#include "utility.h"
+
+class GwpAsanCrashData {
+ public:
+ GwpAsanCrashData() = delete;
+ ~GwpAsanCrashData() = default;
+
+ // Construct the crash data object. Takes a handle to the object that can
+ // supply the memory of the dead process, and pointers to the GWP-ASan state
+ // and metadata regions within that process. Also takes the thread information
+ // of the crashed process. If the process didn't crash via SEGV, GWP-ASan may
+ // still be responsible, as it terminates when it detects an internal error
+ // (double free, invalid free). In these cases, we will retrieve the fault
+ // address from the GWP-ASan allocator's state.
+ GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
+ uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
+
+ // Is GWP-ASan responsible for this crash.
+ bool CrashIsMine() const;
+
+ // Returns the fault address. The fault address may be the same as provided
+ // during construction, or it may have been retrieved from GWP-ASan's internal
+ // allocator crash state.
+ uintptr_t GetFaultAddress() const;
+
+ // Dump the GWP-ASan stringified cause of this crash. May only be called if
+ // CrashIsMine() returns true.
+ void DumpCause(log_t* log) const;
+
+ // Returns whether this crash has a deallocation trace. May only be called if
+ // CrashIsMine() returns true.
+ bool HasDeallocationTrace() const;
+
+ // Dump the GWP-ASan deallocation trace for this crash. May only be called if
+ // HasDeallocationTrace() returns true.
+ void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ // Returns whether this crash has a allocation trace. May only be called if
+ // CrashIsMine() returns true.
+ bool HasAllocationTrace() const;
+
+ // Dump the GWP-ASan allocation trace for this crash. May only be called if
+ // HasAllocationTrace() returns true.
+ void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ protected:
+ // Is GWP-ASan responsible for this crash.
+ bool is_gwp_asan_responsible_ = false;
+
+ // Thread ID of the crash.
+ size_t thread_id_;
+
+ // The type of error that GWP-ASan caused (and the stringified version),
+ // Undefined if GWP-ASan isn't responsible for the crash.
+ gwp_asan::Error error_;
+ const char* error_string_;
+
+ // Pointer to the crash address. Holds the internal crash address if it
+ // exists, otherwise the address provided at construction.
+ uintptr_t crash_address_ = 0u;
+
+ // Pointer to the metadata for the responsible allocation, nullptr if it
+ // doesn't exist.
+ const gwp_asan::AllocationMetadata* responsible_allocation_ = nullptr;
+
+ // Internal state.
+ gwp_asan::AllocatorState state_;
+ std::unique_ptr<const gwp_asan::AllocationMetadata> metadata_;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7133f77..291d994 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -55,6 +55,7 @@
void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
- std::string* amfd_data);
+ std::string* amfd_data, uintptr_t gwp_asan_state,
+ uintptr_t gwp_asan_metadata);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index b33adf3..eed95bc 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android-base/properties.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "libdebuggerd/utility.h"
@@ -31,8 +32,13 @@
#include "host_signal_fixup.h"
#include "log_fake.h"
+// Include tombstone.cpp to define log_tag before GWP-ASan includes log.
#include "tombstone.cpp"
+#include "gwp_asan.cpp"
+
+using ::testing::MatchesRegex;
+
class TombstoneTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -359,3 +365,131 @@
dump_timestamp(&log_, 0);
ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
}
+
+class GwpAsanCrashDataTest : public GwpAsanCrashData {
+public:
+ GwpAsanCrashDataTest(
+ gwp_asan::Error error,
+ const gwp_asan::AllocationMetadata *responsible_allocation) :
+ GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
+ is_gwp_asan_responsible_ = true;
+ error_ = error;
+ responsible_allocation_ = responsible_allocation;
+ error_string_ = gwp_asan::ErrorToString(error_);
+ }
+
+ void SetCrashAddress(uintptr_t crash_address) {
+ crash_address_ = crash_address;
+ }
+};
+
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+ crash_data.SetCrashAddress(0x1000);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(tombstone_contents,
+ MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+ crash_data.SetCrashAddress(0x1000);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(tombstone_contents,
+ MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+ crash_data.SetCrashAddress(0x1025);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+ crash_data.SetCrashAddress(0xffe);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Buffer Underflow, 2 bytes left of a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+ crash_data.SetCrashAddress(0x1001);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+ crash_data.SetCrashAddress(0x1021);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 4e7f35c..fd52e81 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -53,9 +53,13 @@
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/utility.h"
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
using android::base::GetBoolProperty;
using android::base::GetProperty;
using android::base::StringPrintf;
@@ -372,7 +376,8 @@
}
static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
- uint64_t abort_msg_address, bool primary_thread) {
+ uint64_t abort_msg_address, bool primary_thread,
+ const GwpAsanCrashData& gwp_asan_crash_data) {
log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -381,7 +386,13 @@
if (thread_info.siginfo) {
dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
- dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+ }
+
+ if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
+ gwp_asan_crash_data.DumpCause(log);
+ } else if (thread_info.siginfo) {
+ dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
+ thread_info.registers.get());
}
if (primary_thread) {
@@ -402,6 +413,14 @@
}
if (primary_thread) {
+ if (gwp_asan_crash_data.HasDeallocationTrace()) {
+ gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
+ }
+
+ if (gwp_asan_crash_data.HasAllocationTrace()) {
+ gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
+ }
+
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
@@ -583,13 +602,14 @@
}
engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
- nullptr, nullptr);
+ nullptr, nullptr, 0u, 0u);
}
void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
- std::string* amfd_data) {
+ std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
+ uintptr_t gwp_asan_metadata_ptr) {
// don't copy log messages to tombstone unless this is a dev device
bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
@@ -607,7 +627,13 @@
if (it == threads.end()) {
LOG(FATAL) << "failed to find target thread";
}
- dump_thread(&log, unwinder, it->second, abort_msg_address, true);
+
+ GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
+ gwp_asan_state_ptr,
+ gwp_asan_metadata_ptr, it->second);
+
+ dump_thread(&log, unwinder, it->second, abort_msg_address, true,
+ gwp_asan_crash_data);
if (want_logs) {
dump_logs(&log, it->second.pid, 50);
@@ -618,7 +644,7 @@
continue;
}
- dump_thread(&log, unwinder, thread_info, 0, false);
+ dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
}
if (open_files) {
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index bfd0fbb..bf53864 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,10 +95,16 @@
uintptr_t fdsan_table_address;
};
+struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 {
+ uintptr_t gwp_asan_state;
+ uintptr_t gwp_asan_metadata;
+};
+
struct __attribute__((__packed__)) CrashInfo {
CrashInfoHeader header;
union {
CrashInfoDataV1 v1;
CrashInfoDataV2 v2;
+ CrashInfoDataV3 v3;
} data;
};
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b27126b..3a6eb2d 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -234,10 +234,11 @@
LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
if (should_force_check(*fs_stat)) {
ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_forced_argv), e2fsck_forced_argv,
- &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+ &status, false, LOG_KLOG | LOG_FILE, false,
+ FSCK_LOG_FILE);
} else {
ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, &status, false,
- LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+ LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
}
if (ret < 0) {
@@ -256,11 +257,11 @@
if (should_force_check(*fs_stat)) {
LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
- &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+ &status, false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
} else {
LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
- LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+ LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
}
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
@@ -338,7 +339,7 @@
static bool run_tune2fs(const char* argv[], int argc) {
int ret;
- ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, true, nullptr);
+ ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, false, nullptr);
return ret == 0;
}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index a8c2cc1..5aefb7e 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -86,8 +86,8 @@
mke2fs_args.push_back(fs_blkdev.c_str());
mke2fs_args.push_back(size_str.c_str());
- rc = logwrap_fork_execvp(mke2fs_args.size(), mke2fs_args.data(), nullptr, false, LOG_KLOG, true,
- nullptr);
+ rc = logwrap_fork_execvp(mke2fs_args.size(), mke2fs_args.data(), nullptr, false, LOG_KLOG,
+ false, nullptr);
if (rc) {
LERROR << "mke2fs returned " << rc;
return rc;
@@ -97,7 +97,7 @@
"/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
rc = logwrap_fork_execvp(arraysize(e2fsdroid_args), e2fsdroid_args, nullptr, false, LOG_KLOG,
- true, nullptr);
+ false, nullptr);
if (rc) {
LERROR << "e2fsdroid returned " << rc;
}
@@ -135,7 +135,7 @@
args.push_back(fs_blkdev.c_str());
args.push_back(size_str.c_str());
- return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, true, nullptr);
+ return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
}
int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c55e532..83c43c6 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -277,9 +277,9 @@
} else if (StartsWith(flag, "keydirectory=")) {
// The metadata flag is followed by an = and the directory for the keys.
entry->metadata_key_dir = arg;
- } else if (StartsWith(flag, "metadata_cipher=")) {
- // Specify the cipher to use for metadata encryption
- entry->metadata_cipher = arg;
+ } else if (StartsWith(flag, "metadata_encryption=")) {
+ // Specify the cipher and flags to use for metadata encryption
+ entry->metadata_encryption = arg;
} else if (StartsWith(flag, "sysfs_path=")) {
// The path to trigger device gc by idle-maint of vold.
entry->sysfs_path = arg;
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 93bba68..24cbad7 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -80,9 +80,13 @@
return &(*it);
}
+auto verbose = false;
+
void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
const char* file, unsigned int line, const char* message) {
- fprintf(stderr, "%s\n", message);
+ if (verbose || severity == android::base::ERROR || message[0] != '[') {
+ fprintf(stderr, "%s\n", message);
+ }
static auto logd = android::base::LogdLogger();
logd(id, severity, tag, file, line, message);
}
@@ -131,10 +135,14 @@
{"fstab", required_argument, nullptr, 'T'},
{"help", no_argument, nullptr, 'h'},
{"reboot", no_argument, nullptr, 'R'},
+ {"verbose", no_argument, nullptr, 'v'},
{0, 0, nullptr, 0},
};
- for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+ for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
switch (opt) {
+ case 'h':
+ usage(SUCCESS);
+ break;
case 'R':
can_reboot = true;
break;
@@ -145,13 +153,13 @@
}
fstab_file = optarg;
break;
+ case 'v':
+ verbose = true;
+ break;
default:
LOG(ERROR) << "Bad Argument -" << char(opt);
usage(BADARG);
break;
- case 'h':
- usage(SUCCESS);
- break;
}
}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 4dc09c1..087138e 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,7 @@
std::string fs_options;
std::string key_loc;
std::string metadata_key_dir;
- std::string metadata_cipher;
+ std::string metadata_encryption;
off64_t length = 0;
std::string label;
int partnum = -1;
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index d7b689e..6461788 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -280,6 +280,7 @@
extra_argv.emplace_back("allow_discards");
extra_argv.emplace_back("sector_size:4096");
extra_argv.emplace_back("iv_large_sectors");
+ if (is_hw_wrapped_) extra_argv.emplace_back("wrappedkey_v0");
}
if (!extra_argv.empty()) {
argv.emplace_back(std::to_string(extra_argv.size()));
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index affdd29..67af59a 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -526,13 +526,18 @@
bool is_legacy;
ASSERT_TRUE(DmTargetDefaultKey::IsLegacy(&is_legacy));
// set_dun only in the non-is_legacy case
- DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0,
- is_legacy, !is_legacy);
+ DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+ if (is_legacy) {
+ target.SetIsLegacy();
+ } else {
+ target.SetSetDun();
+ }
ASSERT_EQ(target.name(), "default-key");
ASSERT_TRUE(target.Valid());
if (is_legacy) {
ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
} else {
+ // TODO: Add case for wrapped key enabled
ASSERT_EQ(target.GetParameterString(),
"AES-256-XTS abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
"iv_large_sectors");
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index e3dd92b..d2e50d3 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -280,20 +280,20 @@
class DmTargetDefaultKey final : public DmTarget {
public:
DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
- const std::string& key, const std::string& blockdev, uint64_t start_sector,
- bool is_legacy, bool set_dun)
+ const std::string& key, const std::string& blockdev, uint64_t start_sector)
: DmTarget(start, length),
cipher_(cipher),
key_(key),
blockdev_(blockdev),
- start_sector_(start_sector),
- is_legacy_(is_legacy),
- set_dun_(set_dun) {}
+ start_sector_(start_sector) {}
std::string name() const override { return name_; }
bool Valid() const override;
std::string GetParameterString() const override;
static bool IsLegacy(bool* result);
+ void SetIsLegacy() { is_legacy_ = true; }
+ void SetSetDun() { set_dun_ = true; }
+ void SetWrappedKeyV0() { is_hw_wrapped_ = true; }
private:
static const std::string name_;
@@ -301,8 +301,9 @@
std::string key_;
std::string blockdev_;
uint64_t start_sector_;
- bool is_legacy_;
- bool set_dun_;
+ bool is_legacy_ = false;
+ bool set_dun_ = false;
+ bool is_hw_wrapped_ = false;
};
} // namespace dm
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index adfee1d..d274ba4 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -72,6 +72,7 @@
"android/snapshot/snapshot.proto",
"device_info.cpp",
"snapshot.cpp",
+ "snapshot_stats.cpp",
"snapshot_metadata_updater.cpp",
"partition_cow_creator.cpp",
"return.cpp",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index a3a518d..2ac0c44 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -131,3 +131,13 @@
// Sectors allocated for metadata in all the snapshot devices.
uint64 metadata_sectors = 4;
}
+
+// Next: 2
+message SnapshotMergeReport {
+ // Status of the update after the merge attempts.
+ UpdateState state = 1;
+
+ // Number of reboots that occurred after issuing and before completeing the
+ // merge of all the snapshot devices.
+ int32 resume_count = 2;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index ed92dd7..b440c71 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -91,6 +91,8 @@
using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
using FiemapStatus = android::fiemap::FiemapStatus;
+ friend class SnapshotMergeStats;
+
public:
// Dependency injection for testing.
class IDeviceInfo {
@@ -177,7 +179,7 @@
// - Unverified if called on the source slot
// - MergeCompleted if merge is completed
// - other states indicating an error has occurred
- UpdateState InitiateMergeAndWait();
+ UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr);
// Wait for the merge if rebooted into the new slot. Does NOT initiate a
// merge. If the merge has not been initiated (but should be), wait.
@@ -395,6 +397,10 @@
bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
std::string GetStateFilePath() const;
+ // Interact with /metadata/ota/merge_state.
+ // This file contains information related to the snapshot merge process.
+ std::string GetMergeStateFilePath() const;
+
// Helpers for merging.
bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
bool RewriteSnapshotDeviceTable(const std::string& dm_name);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index ba53615..2fe06fb 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -46,6 +46,7 @@
#include "device_info.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
+#include "snapshot_stats.h"
#include "utility.h"
namespace android {
@@ -1739,6 +1740,10 @@
return metadata_dir_ + "/state"s;
}
+std::string SnapshotManager::GetMergeStateFilePath() const {
+ return metadata_dir_ + "/merge_state"s;
+}
+
std::string SnapshotManager::GetLockPath() const {
return metadata_dir_;
}
@@ -2383,7 +2388,7 @@
return AutoUnmountDevice::New(device_->GetMetadataDir());
}
-UpdateState SnapshotManager::InitiateMergeAndWait() {
+UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
{
auto lock = LockExclusive();
// Sync update state from file with bootloader.
@@ -2393,6 +2398,8 @@
}
}
+ SnapshotMergeStats merge_stats(*this);
+
unsigned int last_progress = 0;
auto callback = [&]() -> void {
double progress;
@@ -2405,7 +2412,9 @@
LOG(INFO) << "Waiting for any previous merge request to complete. "
<< "This can take up to several minutes.";
+ merge_stats.Resume();
auto state = ProcessUpdateState(callback);
+ merge_stats.set_state(state);
if (state == UpdateState::None) {
LOG(INFO) << "Can't find any snapshot to merge.";
return state;
@@ -2415,6 +2424,11 @@
LOG(INFO) << "Cannot merge until device reboots.";
return state;
}
+
+ // This is the first snapshot merge that is requested after OTA. We can
+ // initialize the merge duration statistics.
+ merge_stats.Start();
+
if (!InitiateMerge()) {
LOG(ERROR) << "Failed to initiate merge.";
return state;
@@ -2423,9 +2437,13 @@
LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
last_progress = 0;
state = ProcessUpdateState(callback);
+ merge_stats.set_state(state);
}
LOG(INFO) << "Merge finished with state \"" << state << "\".";
+ if (stats_report) {
+ *stats_report = merge_stats.GetReport();
+ }
return state;
}
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
new file mode 100644
index 0000000..c48509e
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -0,0 +1,80 @@
+// Copyright (C) 2020 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.
+
+#include "snapshot_stats.h"
+
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include "utility.h"
+
+namespace android {
+namespace snapshot {
+
+SnapshotMergeStats::SnapshotMergeStats(SnapshotManager& parent) : parent_(parent) {
+ init_time_ = std::chrono::steady_clock::now();
+}
+
+SnapshotMergeStats::~SnapshotMergeStats() {
+ std::string error;
+ auto file_path = parent_.GetMergeStateFilePath();
+ if (!android::base::RemoveFileIfExists(file_path, &error)) {
+ LOG(ERROR) << "Failed to remove merge statistics file " << file_path << ": " << error;
+ return;
+ }
+}
+
+void SnapshotMergeStats::Start() {
+ SnapshotMergeReport report;
+ report.set_resume_count(0);
+ report.set_state(UpdateState::None);
+
+ std::string contents;
+ if (!report.SerializeToString(&contents)) {
+ LOG(ERROR) << "Unable to serialize SnapshotMergeStats.";
+ return;
+ }
+ auto file_path = parent_.GetMergeStateFilePath();
+ if (!WriteStringToFileAtomic(contents, file_path)) {
+ PLOG(ERROR) << "Could not write to merge statistics file";
+ return;
+ }
+}
+
+void SnapshotMergeStats::Resume() {
+ std::string contents;
+ if (!android::base::ReadFileToString(parent_.GetMergeStateFilePath(), &contents)) {
+ PLOG(INFO) << "Read merge statistics file failed";
+ return;
+ }
+
+ if (!report_.ParseFromString(contents)) {
+ LOG(ERROR) << "Unable to parse merge statistics file as SnapshotMergeReport";
+ return;
+ }
+
+ report_.set_resume_count(report_.resume_count() + 1);
+}
+
+void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
+ report_.set_state(state);
+}
+
+SnapshotMergeReport SnapshotMergeStats::GetReport() {
+ return report_;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/snapshot_stats.h
new file mode 100644
index 0000000..1ca9156
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stats.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 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.
+
+#pragma once
+
+#include <chrono>
+
+#include <android/snapshot/snapshot.pb.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotMergeStats {
+ public:
+ SnapshotMergeStats(SnapshotManager& parent);
+ ~SnapshotMergeStats();
+ void Start();
+ void Resume();
+ void set_state(android::snapshot::UpdateState state);
+ SnapshotMergeReport GetReport();
+
+ private:
+ const SnapshotManager& parent_;
+ SnapshotMergeReport report_;
+ std::chrono::time_point<std::chrono::steady_clock> init_time_;
+ std::chrono::time_point<std::chrono::steady_clock> end_time_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index d87274d..5d2840f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1767,6 +1767,7 @@
protected:
void SetUp() override {
if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+ GTEST_SKIP() << "WIP failure b/149738928";
SnapshotTest::SetUp();
userdata_ = std::make_unique<LowSpaceUserdata>();
@@ -1774,6 +1775,7 @@
}
void TearDown() override {
if (!is_virtual_ab_) return;
+ return; // BUG(149738928)
EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
image_manager_->DeleteBackingImage(kImageName));
@@ -1808,10 +1810,6 @@
std::vector<uint64_t> ret;
for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
ret.push_back(size);
-#ifdef SKIP_TEST_IN_PRESUBMIT
- // BUG(148889015);
- break;
-#endif
}
return ret;
}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index e364436..cf324fe 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -902,7 +902,11 @@
done
# If reboot too soon after fresh flash, could trip device update failure logic
-wait_for_screen
+if ! wait_for_screen && ${screen_wait}; then
+ screen_wait=false
+ echo "${ORANGE}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+fi
+
# Can we test remount -R command?
OVERLAYFS_BACKING="cache mnt/scratch"
overlayfs_supported=true
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 800ad7e..69b3150 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -895,11 +895,11 @@
EXPECT_EQ("/dir/key", entry->metadata_key_dir);
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataCipher) {
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source none0 swap defaults keydirectory=/dir/key,metadata_cipher=adiantum
+source none0 swap defaults keydirectory=/dir/key,metadata_encryption=adiantum
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -909,7 +909,28 @@
ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
- EXPECT_EQ("adiantum", entry->metadata_cipher);
+ EXPECT_EQ("adiantum", entry->metadata_encryption);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults keydirectory=/dir/key,metadata_encryption=aes-256-xts:wrappedkey_v0
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
+ auto parts = android::base::Split(entry->metadata_encryption, ":");
+ EXPECT_EQ(2U, parts.size());
+ EXPECT_EQ("aes-256-xts", parts[0]);
+ EXPECT_EQ("wrappedkey_v0", parts[1]);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_SysfsPath) {
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b85f23f..8e9e074 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -55,6 +55,7 @@
using android::hardware::health::V1_0::BatteryHealth;
using android::hardware::health::V1_0::BatteryStatus;
using android::hardware::health::V2_1::BatteryCapacityLevel;
+using android::hardware::health::V2_1::Constants;
namespace android {
@@ -79,6 +80,8 @@
// HIDL enum values are zero initialized, so they need to be initialized
// properly.
health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+ health_info_2_1->batteryChargeTimeToFullNowSeconds =
+ (int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
auto* props = &health_info_2_1->legacy.legacy;
props->batteryStatus = BatteryStatus::UNKNOWN;
props->batteryHealth = BatteryHealth::UNKNOWN;
@@ -134,13 +137,13 @@
{"Normal", BatteryCapacityLevel::NORMAL},
{"High", BatteryCapacityLevel::HIGH},
{"Full", BatteryCapacityLevel::FULL},
- {NULL, BatteryCapacityLevel::UNKNOWN},
+ {NULL, BatteryCapacityLevel::UNSUPPORTED},
};
auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
if (!ret) {
- KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
- *ret = BatteryCapacityLevel::UNKNOWN;
+ KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
+ *ret = BatteryCapacityLevel::UNSUPPORTED;
}
return *ret;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 38e8227..97d40b7 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -548,6 +548,7 @@
skip = strlen("reboot,");
}
SetProperty(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+ WriteStringToFile(reason.c_str() + skip, LAST_REBOOT_REASON_FILE);
sync();
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index cd27eef..24e32d5 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -31,6 +31,7 @@
/* Android reboot reason stored in this property */
#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
+#define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
new file mode 100644
index 0000000..7ef3854
--- /dev/null
+++ b/libcutils/include/private/android_projectid_config.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+/*
+ * This file describes the project ID values we use for filesystem quota
+ * tracking. It is used on devices that don't have the sdcardfs kernel module,
+ * which requires us to use filesystem project IDs for efficient quota
+ * calculation.
+ *
+ * These values are typically set on files and directories using extended
+ * attributes; see vold for examples.
+ */
+
+/* Default project ID for files on external storage. */
+#define PROJECT_ID_EXT_DEFAULT 1000
+/* Project ID for audio files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_AUDIO 1001
+/* Project ID for video files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_VIDEO 1002
+/* Project ID for image files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_IMAGE 1003
+
+/* Start of project IDs for apps to mark external app data. */
+#define PROJECT_ID_EXT_DATA_START 20000
+/* End of project IDs for apps to mark external app data. */
+#define PROJECT_ID_EXT_DATA_END 29999
+
+/* Start of project IDs for apps to mark external cached data. */
+#define PROJECT_ID_EXT_CACHE_START 30000
+/* End of project IDs for apps to mark external cached data. */
+#define PROJECT_ID_EXT_CACHE_END 39999
+
+/* Start of project IDs for apps to mark external OBB data. */
+#define PROJECT_ID_EXT_OBB_START 40000
+/* End of project IDs for apps to mark external OBB data. */
+#define PROJECT_ID_EXT_OBB_END 49999
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index c84ddf7..c98455d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -274,7 +274,7 @@
* Gets the minimum priority that will be logged for this process. If none has been set by a
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
*/
-int __android_log_get_minimum_priority();
+int __android_log_get_minimum_priority(void);
/**
* Sets the default tag if no tag is provided when writing a log message. Defaults to
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f30058a..abd48fc 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -405,7 +405,7 @@
static struct cache2_char security = {
PTHREAD_MUTEX_INITIALIZER, 0,
"persist.logd.security", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
- "ro.device_owner", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+ "ro.organization_owned", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
evaluate_security};
return do_cache2_char(&security);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 75a26bf..a60d2df 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1637,7 +1637,7 @@
TEST(liblog, __security) {
#ifdef __ANDROID__
static const char persist_key[] = "persist.logd.security";
- static const char readonly_key[] = "ro.device_owner";
+ static const char readonly_key[] = "ro.organization_owned";
// A silly default value that can never be in readonly_key so
// that it can be determined the property is not set.
static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
@@ -1657,7 +1657,7 @@
if (!strcmp(readonly, nothing_val)) {
// Lets check if we can set the value (we should not be allowed to do so)
EXPECT_FALSE(__android_log_security());
- fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
+ fprintf(stderr, "WARNING: setting ro.organization_owned to a domain\n");
static const char domain[] = "com.google.android.SecOps.DeviceOwner";
EXPECT_NE(0, property_set(readonly_key, domain));
useconds_t total_time = 0;
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 0b38b6b..4aa439a 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -30,8 +30,7 @@
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
-bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
- bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
#ifndef __ANDROID_VNDK__
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6272664..d669ebe 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -115,9 +115,8 @@
TaskProfiles::GetInstance().DropResourceCaching();
}
-bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
- bool use_fd_cache) {
- return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, use_fd_cache);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+ return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
}
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 72f01af..4af4589 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -201,22 +201,6 @@
}
bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (IsFdValid()) {
- // fd is cached, reuse it
- if (!AddTidToCgroup(pid, fd_)) {
- LOG(ERROR) << "Failed to add task into cgroup";
- return false;
- }
- return true;
- }
-
- if (fd_ == FDS_INACCESSIBLE) {
- // no permissions to access the file, ignore
- return true;
- }
-
- // this is app-dependent path and fd is not cached or cached fd can't be used
std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
@@ -270,7 +254,6 @@
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
- profile->EnableResourceCaching();
if (!profile->ExecuteForProcess(uid, pid)) {
PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
}
@@ -280,7 +263,6 @@
bool ApplyProfileAction::ExecuteForTask(int tid) const {
for (const auto& profile : profiles_) {
- profile->EnableResourceCaching();
if (!profile->ExecuteForTask(tid)) {
PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
}
@@ -288,6 +270,18 @@
return true;
}
+void ApplyProfileAction::EnableResourceCaching() {
+ for (const auto& profile : profiles_) {
+ profile->EnableResourceCaching();
+ }
+}
+
+void ApplyProfileAction::DropResourceCaching() {
+ for (const auto& profile : profiles_) {
+ profile->DropResourceCaching();
+ }
+}
+
void TaskProfile::MoveTo(TaskProfile* profile) {
profile->elements_ = std::move(elements_);
profile->res_cached_ = res_cached_;
@@ -527,13 +521,10 @@
}
bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
- const std::vector<std::string>& profiles, bool use_fd_cache) {
+ const std::vector<std::string>& profiles) {
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
if (profile != nullptr) {
- if (use_fd_cache) {
- profile->EnableResourceCaching();
- }
if (!profile->ExecuteForProcess(uid, pid)) {
PLOG(WARNING) << "Failed to apply " << name << " process profile";
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index a64ca50..28bc00c 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -163,6 +163,8 @@
virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
virtual bool ExecuteForTask(int tid) const;
+ virtual void EnableResourceCaching();
+ virtual void DropResourceCaching();
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -176,8 +178,7 @@
TaskProfile* GetProfile(const std::string& name) const;
const ProfileAttribute* GetAttribute(const std::string& name) const;
void DropResourceCaching() const;
- bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
- bool use_fd_cache);
+ bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
private:
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index ccda5d1..627f0d4 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -39,6 +39,10 @@
"clang-analyzer-security*",
"android-*",
],
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
}
cc_test {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 08e3d22..76a970f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1129,7 +1129,7 @@
if (!ret) {
error(EXIT_FAILURE, 0, R"init(Unexpected EOF!
-This means that either logd crashed, or more likely, this instance of logcat was unable to read log
+This means that either the device shut down, logd crashed, or this instance of logcat was unable to read log
messages as quickly as they were being produced.
If you have enabled significant logging, look into using the -G option to increase log buffer sizes.)init");
diff --git a/logd/README.property b/logd/README.property
index d2a2cbb..1b7e165 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -6,7 +6,7 @@
ro.logd.auditd.main bool true selinux audit messages sent to main.
ro.logd.auditd.events bool true selinux audit messages sent to events.
persist.logd.security bool false Enable security buffer.
-ro.device_owner bool false Override persist.logd.security to false
+ro.organization_owned bool false Override persist.logd.security to false
ro.logd.kernel bool+ svelte+ Enable klogd daemon
ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
ro.debuggable number if not "1", logd.statistics &
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f4a846f..a643062 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -317,7 +317,7 @@
{"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
{"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
{"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
- {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+ {"ro.organization_owned", "u:object_r:device_logging_prop:s0", "string", false},
{"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
@@ -669,7 +669,7 @@
{"ro.crypto.type", "u:object_r:vold_prop:s0"},
{"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
{"ro.debuggable", "u:object_r:default_prop:s0"},
- {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+ {"ro.organization_owned", "u:object_r:device_logging_prop:s0"},
{"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
{"ro.frp.pst", "u:object_r:default_prop:s0"},
{"ro.hardware", "u:object_r:default_prop:s0"},
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 782f967..91dd7a5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -386,6 +386,10 @@
# Start logd before any other services run to ensure we capture all of their logs.
start logd
# Start lmkd before any other services run so that it can register them
+ chown root system /sys/module/lowmemorykiller/parameters/adj
+ chmod 0664 /sys/module/lowmemorykiller/parameters/adj
+ chown root system /sys/module/lowmemorykiller/parameters/minfree
+ chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
start lmkd
# Start essential services.
@@ -496,6 +500,7 @@
mkdir /metadata/vold
chmod 0700 /metadata/vold
mkdir /metadata/password_slots 0771 root system
+ mkdir /metadata/bootstat 0750 system log
mkdir /metadata/ota 0700 root system
mkdir /metadata/ota/snapshots 0700 root system
@@ -824,10 +829,6 @@
# parameters to match how it is managing things.
write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
- chown root system /sys/module/lowmemorykiller/parameters/adj
- chmod 0664 /sys/module/lowmemorykiller/parameters/adj
- chown root system /sys/module/lowmemorykiller/parameters/minfree
- chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
# System server manages zram writeback
chown root system /sys/block/zram0/idle