// byte. The client sent strings with null termination, which post-string_view, start
// being interpreted as part of the string, unless we explicitly strip them.
address = StripTrailingNulls(address);
-
+#if ADB_HOST
+ // The incoming address (from the payload) might be some other
+ // target (e.g tcp:<ip>:8000), however we do not allow *any*
+ // such requests - namely, those from (a potentially compromised)
+ // adbd (reverse:forward: source) port transport.
+ if (!t->IsReverseConfigured(address.data())) {
+ LOG(FATAL) << __func__ << " disallowed connect to " << address << " from "
+ << t->serial_name();
+ }
+#endif
asocket* s = create_local_service_socket(address, t);
if (s == nullptr) {
send_close(0, p->msg.arg0, t);
}
void connect_to_remote(asocket* s, std::string_view destination) {
+#if ADB_HOST
+ // Snoop reverse:forward: requests to track them so that an
+ // appropriate filter (to figure out whether the remote is
+ // allowed to connect locally) can be applied.
+ s->transport->UpdateReverseConfig(destination);
+#endif
D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
apacket* p = get_apacket();
return t->GetUsbHandle() == usb && t->GetConnectionState() == kCsNoPerm;
});
}
+
+// Track reverse:forward commands, so that info can be used to develop
+// an 'allow-list':
+// - adb reverse tcp:<device_port> localhost:<host_port> : responds with the
+// device_port
+// - adb reverse --remove tcp:<device_port> : responds OKAY
+// - adb reverse --remove-all : responds OKAY
+void atransport::UpdateReverseConfig(std::string_view service_addr) {
+ check_main_thread();
+ if (!android::base::ConsumePrefix(&service_addr, "reverse:")) {
+ return;
+ }
+
+ if (android::base::ConsumePrefix(&service_addr, "forward:")) {
+ // forward:[norebind:]<remote>;<local>
+ bool norebind = android::base::ConsumePrefix(&service_addr, "norebind:");
+ auto it = service_addr.find(';');
+ if (it == std::string::npos) {
+ return;
+ }
+ std::string remote(service_addr.substr(0, it));
+
+ if (norebind && reverse_forwards_.find(remote) != reverse_forwards_.end()) {
+ // This will fail, don't update the map.
+ LOG(DEBUG) << "ignoring reverse forward that will fail due to norebind";
+ return;
+ }
+
+ std::string local(service_addr.substr(it + 1));
+ reverse_forwards_[remote] = local;
+ } else if (android::base::ConsumePrefix(&service_addr, "killforward:")) {
+ // kill-forward:<remote>
+ auto it = service_addr.find(';');
+ if (it != std::string::npos) {
+ return;
+ }
+ reverse_forwards_.erase(std::string(service_addr));
+ } else if (service_addr == "killforward-all") {
+ reverse_forwards_.clear();
+ } else if (service_addr == "list-forward") {
+ LOG(DEBUG) << __func__ << " ignoring --list";
+ } else { // Anything else we need to know about?
+ LOG(FATAL) << "unhandled reverse service: " << service_addr;
+ }
+}
+
+// Is this an authorized :connect request?
+bool atransport::IsReverseConfigured(const std::string& local_addr) {
+ check_main_thread();
+ for (const auto& [remote, local] : reverse_forwards_) {
+ if (local == local_addr) {
+ return true;
+ }
+ }
+ return false;
+}
+
#endif
bool check_header(apacket* p, atransport* t) {
#include <string>
#include <string_view>
#include <thread>
+#include <unordered_map>
#include <unordered_set>
#include <android-base/macros.h>
void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
usb_handle* GetUsbHandle() { return usb_handle_; }
+ // Interface for management/filter on forward:reverse: configuration.
+ void UpdateReverseConfig(std::string_view service_addr);
+ bool IsReverseConfigured(const std::string& local_addr);
+
const TransportId id;
bool online = false;
std::mutex mutex_;
+#if ADB_HOST
+ // Track remote addresses against local addresses (configured)
+ // through `adb reverse` commands.
+ // Access constrained to primary thread by virtue of check_main_thread().
+ std::unordered_map<std::string, std::string> reverse_forwards_;
+#endif
+
DISALLOW_COPY_AND_ASSIGN(atransport);
};