| /* |
| * 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. |
| */ |
| |
| #define ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION |
| |
| #include "include/adbd_auth.h" |
| |
| #include "adbd_auth_internal.h" |
| |
| using android::base::unique_fd; |
| |
| static constexpr uint32_t kAuthVersion = 3; |
| |
| static std::set<AdbdAuthFeature> supported_features = { |
| AdbdAuthFeature::WifiLifeCycle}; |
| |
| AdbdAuthContext::AdbdAuthContext(const AdbdAuthCallbacksV1* callbacks, std::optional<int> server_fd) |
| : next_id_(0), callbacks_(*callbacks) { |
| epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC)); |
| if (epoll_fd_ == -1) { |
| PLOG(FATAL) << "adbd_auth: failed to create epoll fd"; |
| } |
| |
| interrupt_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); |
| if (interrupt_fd_ == -1) { |
| PLOG(FATAL) << "adbd_auth: failed to create interrupt_fd"; |
| } |
| |
| if (server_fd.has_value()) { |
| sock_fd_.reset(server_fd.value()); |
| } else { |
| sock_fd_.reset(android_get_control_socket("adbd")); |
| } |
| |
| if (sock_fd_ == -1) { |
| PLOG(ERROR) << "adbd_auth: failed to get adbd authentication socket"; |
| } else { |
| if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) { |
| PLOG(FATAL) |
| << "adbd_auth: failed to make adbd authentication socket cloexec"; |
| } |
| |
| if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) { |
| PLOG(FATAL) |
| << "adbd_auth: failed to make adbd authentication socket nonblocking"; |
| } |
| |
| if (listen(sock_fd_.get(), 4) != 0) { |
| PLOG(FATAL) |
| << "adbd_auth: failed to listen on adbd authentication socket"; |
| } |
| } |
| } |
| |
| void AdbdAuthContext::DispatchPendingPrompt() REQUIRES(mutex_) { |
| if (dispatched_prompt_) { |
| LOG(INFO) << "adbd_auth: prompt currently pending, skipping"; |
| return; |
| } |
| |
| if (pending_prompts_.empty()) { |
| LOG(INFO) << "adbd_auth: no prompts to send"; |
| return; |
| } |
| |
| LOG(INFO) << "adbd_auth: prompting user for adb authentication"; |
| auto [id, public_key, arg] = std::move(pending_prompts_.front()); |
| pending_prompts_.pop_front(); |
| |
| this->output_queue_.emplace_back( |
| AdbdAuthPacketRequestAuthorization{.public_key = public_key}); |
| |
| Interrupt(); |
| dispatched_prompt_ = std::make_tuple(id, public_key, arg); |
| } |
| |
| void AdbdAuthContext::UpdateFrameworkWritable() REQUIRES(mutex_) { |
| // This might result in redundant calls to EPOLL_CTL_MOD if, for example, we |
| // get notified at the same time as a framework connection, but that's |
| // unlikely and this doesn't need to be fast anyway. |
| if (framework_fd_ != -1) { |
| struct epoll_event event; |
| event.events = EPOLLIN; |
| if (!output_queue_.empty()) { |
| LOG(INFO) << "adbd_auth: marking framework writable"; |
| event.events |= EPOLLOUT; |
| } |
| event.data.u64 = kEpollConstFramework; |
| CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, framework_fd_.get(), |
| &event)); |
| } |
| } |
| |
| void AdbdAuthContext::ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) { |
| LOG(INFO) << "adbd_auth: received new framework fd " << new_fd.get() |
| << " (current = " << framework_fd_.get() << ")"; |
| |
| // If we already had a framework fd, clean up after ourselves. |
| if (framework_fd_ != -1) { |
| output_queue_.clear(); |
| dispatched_prompt_.reset(); |
| CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, framework_fd_.get(), |
| nullptr)); |
| framework_fd_.reset(); |
| } |
| |
| if (new_fd != -1) { |
| struct epoll_event event; |
| event.events = EPOLLIN; |
| if (!output_queue_.empty()) { |
| LOG(INFO) << "adbd_auth: marking framework writable"; |
| event.events |= EPOLLOUT; |
| } |
| event.data.u64 = kEpollConstFramework; |
| CHECK_EQ(0, |
| epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, new_fd.get(), &event)); |
| framework_fd_ = std::move(new_fd); |
| } |
| } |
| |
| void AdbdAuthContext::HandlePacket(std::string_view packet) EXCLUDES(mutex_) { |
| LOG(INFO) << "adbd_auth: received packet: " << packet; |
| |
| received_packets_++; |
| if (packet.size() < 2) { |
| LOG(ERROR) << "adbd_auth: received packet of invalid length"; |
| std::lock_guard<std::mutex> lock(mutex_); |
| ReplaceFrameworkFd(unique_fd()); |
| } |
| |
| bool handled_packet = false; |
| for (size_t i = 0; i < framework_handlers_.size(); ++i) { |
| if (android::base::ConsumePrefix(&packet, framework_handlers_[i].code)) { |
| framework_handlers_[i].cb(packet); |
| handled_packet = true; |
| break; |
| } |
| } |
| if (!handled_packet) { |
| LOG(ERROR) << "adbd_auth: unhandled packet: " << packet; |
| } |
| } |
| |
| void AdbdAuthContext::AllowUsbDevice(std::string_view buf) EXCLUDES(mutex_) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| CHECK(buf.empty()); |
| |
| if (dispatched_prompt_.has_value()) { |
| // It's possible for the framework to send us a response without our having |
| // sent a request to it: e.g. if adbd restarts while we have a pending |
| // request. |
| auto& [id, key, arg] = *dispatched_prompt_; |
| keys_.emplace(id, std::move(key)); |
| |
| callbacks_.key_authorized(arg, id); |
| dispatched_prompt_ = std::nullopt; |
| } else { |
| LOG(WARNING) |
| << "adbd_auth: received authorization for unknown prompt, ignoring"; |
| } |
| |
| // We need to dispatch pending prompts here upon success as well, |
| // since we might have multiple queued prompts. |
| DispatchPendingPrompt(); |
| } |
| |
| void AdbdAuthContext::DenyUsbDevice(std::string_view buf) EXCLUDES(mutex_) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| CHECK(buf.empty()); |
| // TODO: Do we want a callback if the key is denied? |
| dispatched_prompt_ = std::nullopt; |
| DispatchPendingPrompt(); |
| } |
| |
| void AdbdAuthContext::KeyRemoved(std::string_view buf) EXCLUDES(mutex_) { |
| CHECK(!buf.empty()); |
| callbacks_.key_removed(buf.data(), buf.size()); |
| } |
| |
| bool AdbdAuthContext::SendPacket() REQUIRES(mutex_) { |
| if (output_queue_.empty()) { |
| return false; |
| } |
| |
| CHECK_NE(-1, framework_fd_.get()); |
| |
| auto& packet = output_queue_.front(); |
| struct iovec iovs[6]; |
| int iovcnt = 2; |
| // A scrap buffer to store bytes if they are needed in an iovec.base |
| uint8_t bytes[2]; |
| |
| if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) { |
| iovs[0].iov_base = const_cast<char*>("CK"); |
| iovs[0].iov_len = 2; |
| iovs[1].iov_base = p->public_key.data(); |
| iovs[1].iov_len = p->public_key.size(); |
| } else if (auto* p = std::get_if<AdbdAuthPacketDisconnected>(&packet)) { |
| iovs[0].iov_base = const_cast<char*>("DC"); |
| iovs[0].iov_len = 2; |
| iovs[1].iov_base = p->public_key.data(); |
| iovs[1].iov_len = p->public_key.size(); |
| } else if (auto* p = |
| std::get_if<AdbdAuthPacketRequestAuthorization>(&packet)) { |
| iovs[0].iov_base = const_cast<char*>("PK"); |
| iovs[0].iov_len = 2; |
| iovs[1].iov_base = p->public_key.data(); |
| iovs[1].iov_len = p->public_key.size(); |
| } else if (auto* p = std::get_if<AdbdPacketTlsDeviceConnected>(&packet)) { |
| iovcnt = 3; |
| iovs[0].iov_base = const_cast<char*>("WE"); |
| iovs[0].iov_len = 2; |
| iovs[1].iov_base = &p->transport_type; |
| iovs[1].iov_len = 1; |
| iovs[2].iov_base = p->public_key.data(); |
| iovs[2].iov_len = p->public_key.size(); |
| } else if (auto* p = std::get_if<AdbdPacketTlsDeviceDisconnected>(&packet)) { |
| iovcnt = 3; |
| iovs[0].iov_base = const_cast<char*>("WF"); |
| iovs[0].iov_len = 2; |
| iovs[1].iov_base = &p->transport_type; |
| iovs[1].iov_len = 1; |
| iovs[2].iov_base = p->public_key.data(); |
| iovs[2].iov_len = p->public_key.size(); |
| } else if (auto* p = std::get_if<AdbdPacketTlsServerPort>(&packet)) { |
| iovcnt = 2; |
| iovs[0].iov_base = const_cast<char*>("TP"); |
| iovs[0].iov_len = 2; |
| iovs[1].iov_base = &p->port; |
| iovs[1].iov_len = 2; |
| } else if (auto* p = std::get_if<AdbdPacketRegisterService>(&packet)) { |
| iovcnt = 6; |
| iovs[0].iov_base = const_cast<char*>("RS"); |
| iovs[0].iov_len = 2; |
| |
| bytes[0] = p->instance_name.size(); |
| iovs[1].iov_base = &bytes[0]; |
| iovs[1].iov_len = 1; |
| iovs[2].iov_base = p->instance_name.data(); |
| iovs[2].iov_len = p->instance_name.size(); |
| |
| bytes[1] = p->service_type.size(); |
| iovs[3].iov_base = &bytes[1]; |
| iovs[3].iov_len = 1; |
| iovs[4].iov_base = p->service_type.data(); |
| iovs[4].iov_len = p->service_type.size(); |
| |
| iovs[5].iov_base = &p->port; |
| iovs[5].iov_len = 2; |
| } else if (auto* p = std::get_if<AdbdPacketUnregisterService>(&packet)) { |
| iovcnt = 5; |
| iovs[0].iov_base = const_cast<char*>("US"); |
| iovs[0].iov_len = 2; |
| |
| bytes[0] = p->instance_name.size(); |
| iovs[1].iov_base = &bytes[0]; |
| iovs[1].iov_len = 1; |
| iovs[2].iov_base = p->instance_name.data(); |
| iovs[2].iov_len = p->instance_name.size(); |
| |
| bytes[1] = p->service_type.size(); |
| iovs[3].iov_base = &bytes[1]; |
| iovs[3].iov_len = 1; |
| iovs[4].iov_base = p->service_type.data(); |
| iovs[4].iov_len = p->service_type.size(); |
| |
| } else { |
| LOG(FATAL) << "adbd_auth: unhandled packet type?"; |
| } |
| |
| LOG(INFO) << "adbd_auth: sending packet: " |
| << std::string((const char*)iovs[0].iov_base, 2); |
| |
| ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt); |
| output_queue_.pop_front(); |
| if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { |
| PLOG(ERROR) << "adbd_auth: failed to write to framework fd"; |
| ReplaceFrameworkFd(unique_fd()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void AdbdAuthContext::Run() { |
| if (sock_fd_ == -1) { |
| LOG(ERROR) << "adbd_auth: socket unavailable, disabling user prompts"; |
| } else { |
| struct epoll_event event; |
| event.events = EPOLLIN; |
| event.data.u64 = kEpollConstSocket; |
| CHECK_EQ(0, |
| epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, sock_fd_.get(), &event)); |
| } |
| |
| { |
| struct epoll_event event; |
| event.events = EPOLLIN; |
| event.data.u64 = kEpollConstEventFd; |
| CHECK_EQ( |
| 0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, interrupt_fd_.get(), &event)); |
| } |
| |
| running_ = true; |
| |
| while (running_) { |
| struct epoll_event events[3]; |
| int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1)); |
| if (rc == -1) { |
| PLOG(FATAL) << "adbd_auth: epoll_wait failed"; |
| } else if (rc == 0) { |
| LOG(FATAL) << "adbd_auth: epoll_wait returned 0"; |
| } |
| |
| bool restart = false; |
| for (int i = 0; i < rc; ++i) { |
| if (restart) { |
| break; |
| } |
| |
| struct epoll_event& event = events[i]; |
| switch (event.data.u64) { |
| case kEpollConstSocket: { |
| unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr, |
| SOCK_CLOEXEC | SOCK_NONBLOCK)); |
| if (new_framework_fd == -1) { |
| PLOG(FATAL) << "adbd_auth: failed to accept framework fd"; |
| } |
| |
| LOG(INFO) << "adbd_auth: received a new framework connection"; |
| std::lock_guard<std::mutex> lock(mutex_); |
| ReplaceFrameworkFd(std::move(new_framework_fd)); |
| |
| this->on_framework_connected(); |
| |
| // Stop iterating over events: one of the later ones might be the old |
| // framework fd. |
| restart = false; |
| break; |
| } |
| |
| case kEpollConstEventFd: { |
| // We were woken up to write something. |
| uint64_t dummy; |
| int rc = |
| TEMP_FAILURE_RETRY(read(interrupt_fd_.get(), &dummy, sizeof(dummy))); |
| if (rc != 8) { |
| PLOG(FATAL) << "adbd_auth: failed to read from eventfd (rc = " << rc |
| << ")"; |
| } |
| |
| std::lock_guard<std::mutex> lock(mutex_); |
| UpdateFrameworkWritable(); |
| break; |
| } |
| |
| case kEpollConstFramework: { |
| char buf[4096]; |
| if (event.events & EPOLLIN) { |
| int rc = |
| TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf))); |
| if (rc == -1) { |
| PLOG(FATAL) << "adbd_auth: failed to read from framework fd"; |
| } else if (rc == 0) { |
| LOG(INFO) << "adbd_auth: hit EOF on framework fd"; |
| std::lock_guard<std::mutex> lock(mutex_); |
| ReplaceFrameworkFd(unique_fd()); |
| } else { |
| HandlePacket(std::string_view(buf, rc)); |
| } |
| } |
| |
| if (event.events & EPOLLOUT) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| while (SendPacket()) { |
| continue; |
| } |
| UpdateFrameworkWritable(); |
| } |
| |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| void AdbdAuthContext::Stop() { |
| running_ = false; |
| Interrupt(); |
| } |
| |
| static constexpr std::pair<const char*, bool> key_paths[] = { |
| {"/adb_keys", true /* follow symlinks */}, |
| {"/data/misc/adb/adb_keys", false /* don't follow symlinks */}, |
| }; |
| void AdbdAuthContext::IteratePublicKeys(bool (*callback)(void*, const char*, |
| size_t), |
| void* opaque) { |
| for (const auto& [path, follow_symlinks] : key_paths) { |
| if (access(path, R_OK) == 0) { |
| LOG(INFO) << "adbd_auth: loading keys from " << path; |
| std::string content; |
| if (!android::base::ReadFileToString(path, &content, follow_symlinks)) { |
| PLOG(ERROR) << "adbd_auth: couldn't read " << path; |
| continue; |
| } |
| for (const auto& line : android::base::Split(content, "\n")) { |
| if (!callback(opaque, line.data(), line.size())) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| uint64_t AdbdAuthContext::PromptUser(std::string_view public_key, void* arg) |
| EXCLUDES(mutex_) { |
| uint64_t id = NextId(); |
| |
| std::lock_guard<std::mutex> lock(mutex_); |
| LOG(INFO) << "adbd_auth: sending prompt with id " << id; |
| pending_prompts_.emplace_back(id, public_key, arg); |
| DispatchPendingPrompt(); |
| return id; |
| } |
| |
| uint64_t AdbdAuthContext::NotifyAuthenticated(std::string_view public_key) |
| EXCLUDES(mutex_) { |
| uint64_t id = NextId(); |
| std::lock_guard<std::mutex> lock(mutex_); |
| keys_.emplace(id, public_key); |
| output_queue_.emplace_back( |
| AdbdAuthPacketAuthenticated{.public_key = std::string(public_key)}); |
| return id; |
| } |
| |
| void AdbdAuthContext::NotifyDisconnected(uint64_t id) EXCLUDES(mutex_) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| auto it = keys_.find(id); |
| if (it == keys_.end()) { |
| LOG(DEBUG) << "adbd_auth: couldn't find public key to notify " |
| "disconnection, skipping"; |
| return; |
| } |
| output_queue_.emplace_back( |
| AdbdAuthPacketDisconnected{.public_key = std::move(it->second)}); |
| keys_.erase(it); |
| } |
| |
| uint64_t AdbdAuthContext::NotifyTlsDeviceConnected(AdbTransportType type, |
| std::string_view public_key) |
| EXCLUDES(mutex_) { |
| uint64_t id = NextId(); |
| std::lock_guard<std::mutex> lock(mutex_); |
| keys_.emplace(id, public_key); |
| output_queue_.emplace_back( |
| AdbdPacketTlsDeviceConnected{.transport_type = static_cast<uint8_t>(type), |
| .public_key = std::string(public_key)}); |
| Interrupt(); |
| return id; |
| } |
| |
| void AdbdAuthContext::NotifyTlsDeviceDisconnected(AdbTransportType type, |
| uint64_t id) |
| EXCLUDES(mutex_) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| auto it = keys_.find(id); |
| if (it == keys_.end()) { |
| LOG(DEBUG) |
| << "adbd_auth: couldn't find public key to notify disconnection of tls " |
| "device, skipping"; |
| return; |
| } |
| output_queue_.emplace_back(AdbdPacketTlsDeviceDisconnected{ |
| .transport_type = static_cast<uint8_t>(type), |
| .public_key = std::move(it->second)}); |
| keys_.erase(it); |
| Interrupt(); |
| } |
| |
| void AdbdAuthContext::SendTLSServerPort(uint16_t port) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| output_queue_.emplace_back(AdbdPacketTlsServerPort{.port = port}); |
| Interrupt(); |
| } |
| |
| void AdbdAuthContext::SendRegisterService(const char* instance_name, const char* service_type, |
| uint16_t port) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| output_queue_.emplace_back(AdbdPacketRegisterService{ |
| .instance_name = instance_name, |
| .service_type = service_type, |
| .port = port}); |
| Interrupt(); |
| } |
| |
| void AdbdAuthContext::SendUnregisterService(const char* instance_name, const char* service_type) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| output_queue_.emplace_back(AdbdPacketUnregisterService{ |
| .instance_name = instance_name, |
| .service_type = service_type}); |
| Interrupt(); |
| } |
| |
| // Interrupt the worker thread to do some work. |
| void AdbdAuthContext::Interrupt() { |
| uint64_t value = 1; |
| ssize_t rc = write(interrupt_fd_.get(), &value, sizeof(value)); |
| if (rc == -1) { |
| PLOG(FATAL) << "adbd_auth: write to interrupt_fd failed"; |
| } else if (rc != sizeof(value)) { |
| LOG(FATAL) << "adbd_auth: write to interrupt_fd returned short (" << rc << ")"; |
| } |
| } |
| |
| void AdbdAuthContext::InitFrameworkHandlers() { |
| // Framework wants to disconnect from a secured wifi device |
| framework_handlers_.emplace_back( |
| FrameworkPktHandler{.code = "DD", |
| .cb = std::bind(&AdbdAuthContext::KeyRemoved, this, |
| std::placeholders::_1)}); |
| // Framework allows USB debugging for the device |
| framework_handlers_.emplace_back( |
| FrameworkPktHandler{.code = "OK", |
| .cb = std::bind(&AdbdAuthContext::AllowUsbDevice, |
| this, std::placeholders::_1)}); |
| // Framework denies USB debugging for the device |
| framework_handlers_.emplace_back( |
| FrameworkPktHandler{.code = "NO", |
| .cb = std::bind(&AdbdAuthContext::DenyUsbDevice, this, |
| std::placeholders::_1)}); |
| } |
| |
| AdbdAuthContextV2::AdbdAuthContextV2(const AdbdAuthCallbacksV2* callbacks, std::optional<int> server_fd) |
| : AdbdAuthContext(callbacks, server_fd), callbacks_v2_(*callbacks) {} |
| |
| void AdbdAuthContextV2::InitFrameworkHandlers() { |
| AdbdAuthContext::InitFrameworkHandlers(); |
| // Framework requires ADB Wifi to start |
| framework_handlers_.emplace_back( |
| FrameworkPktHandler{.code = "W1", |
| .cb = std::bind(&AdbdAuthContextV2::StartAdbWifi, |
| this, std::placeholders::_1)}); |
| |
| // Framework requires ADB Wifi to stop |
| framework_handlers_.emplace_back( |
| FrameworkPktHandler{.code = "W0", |
| .cb = std::bind(&AdbdAuthContextV2::StopAdbWifi, this, |
| std::placeholders::_1)}); |
| } |
| |
| void AdbdAuthContextV2::StartAdbWifi(std::string_view buf) EXCLUDES(mutex_) { |
| CHECK(buf.empty()); |
| callbacks_v2_.start_adbd_wifi(); |
| } |
| |
| void AdbdAuthContextV2::StopAdbWifi(std::string_view buf) EXCLUDES(mutex_) { |
| CHECK(buf.empty()); |
| callbacks_v2_.stop_adbd_wifi(); |
| } |
| |
| AdbdAuthContextV3::AdbdAuthContextV3(const AdbdAuthCallbacksV3* callbacks, std::optional<int> server_fd) |
| : AdbdAuthContextV2(callbacks, server_fd), callbacks_v3_(*callbacks) {} |
| |
| AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) { |
| AdbdAuthContext* ctx = nullptr; |
| switch (callbacks->version) { |
| case 1: { |
| ctx = new AdbdAuthContext( reinterpret_cast<AdbdAuthCallbacksV1*>(callbacks)); |
| break; |
| } |
| case 2: { |
| ctx = new AdbdAuthContextV2( reinterpret_cast<AdbdAuthCallbacksV2*>(callbacks)); |
| break; |
| } |
| case kAuthVersion : { |
| ctx = new AdbdAuthContextV3( reinterpret_cast<AdbdAuthCallbacksV3*>(callbacks)); |
| break; |
| } |
| default: { |
| LOG(FATAL) << "adbd_auth: unknown AdbdAuthCallbacks version " << callbacks->version; |
| return nullptr; |
| } |
| } |
| |
| ctx->InitFrameworkHandlers(); |
| return ctx; |
| } |
| |
| void adbd_auth_delete(AdbdAuthContext* ctx) { delete ctx; } |
| |
| void adbd_auth_run(AdbdAuthContext* ctx) { return ctx->Run(); } |
| |
| void adbd_auth_get_public_keys(AdbdAuthContext* ctx, |
| bool (*callback)(void* opaque, |
| const char* public_key, |
| size_t len), |
| void* opaque) { |
| ctx->IteratePublicKeys(callback, opaque); |
| } |
| |
| uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, |
| size_t len) { |
| return ctx->NotifyAuthenticated(std::string_view(public_key, len)); |
| } |
| |
| void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) { |
| return ctx->NotifyDisconnected(id); |
| } |
| |
| void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, |
| size_t len, void* opaque) { |
| adbd_auth_prompt_user_with_id(ctx, public_key, len, opaque); |
| } |
| |
| uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx, |
| const char* public_key, size_t len, |
| void* opaque) { |
| return ctx->PromptUser(std::string_view(public_key, len), opaque); |
| } |
| |
| uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx, |
| AdbTransportType type, |
| const char* public_key, size_t len) { |
| return ctx->NotifyTlsDeviceConnected(type, std::string_view(public_key, len)); |
| } |
| |
| void adbd_auth_tls_device_disconnected(AdbdAuthContext* ctx, |
| AdbTransportType type, uint64_t id) { |
| ctx->NotifyTlsDeviceDisconnected(type, id); |
| } |
| |
| uint32_t adbd_auth_get_max_version() { return kAuthVersion; } |
| |
| bool adbd_auth_supports_feature(AdbdAuthFeature f) { |
| return supported_features.contains(f); |
| } |
| |
| void adbd_auth_send_tls_server_port(AdbdAuthContext* ctx, uint16_t port) { |
| ctx->SendTLSServerPort(port); |
| } |
| |
| static bool validateMdnsNames(const char* instance_name, const char* service_type) { |
| if (strlen(instance_name) > 255 || strlen(service_type) > 255) { |
| LOG(ERROR) << "mDNS name is too long: instance='" |
| << instance_name << "', service='" << service_type << "'"; |
| return false; |
| } |
| return true; |
| } |
| |
| // RFC 1035: Domain Names - Implementation and Specification |
| // "To simplify implementations, the total length of a domain name (i.e., |
| // label octets and label length octets) is restricted to 255 octets or |
| // less." |
| // |
| // RFC 6762: Multicast DNS |
| // Multicast DNS borrows heavily from the existing DNS protocol |
| // [RFC1034] [RFC1035] [RFC6195], using the existing DNS message |
| // structure, name syntax, and resource record types. |
| AdbdauthRegisterResult adbd_auth_register_service(AdbdAuthContext* ctx, |
| const char* instance_name, |
| const char* service_type, |
| uint16_t port) { |
| if (!validateMdnsNames(instance_name, service_type)) { |
| return ADBD_AUTH_REGISTER_BAD_NAME; |
| } |
| ctx->SendRegisterService(instance_name, service_type, port); |
| return ADBD_AUTH_REGISTER_OK; |
| } |
| |
| // See adbd_auth_register_service for name length restrictions. |
| AdbdauthUnregisterResult adbd_auth_unregister_service(AdbdAuthContext* ctx, |
| const char* instance_name, |
| const char* service_type) { |
| if (!validateMdnsNames(instance_name, service_type)) { |
| return ADBD_AUTH_UNREGISTER_BAD_NAME; |
| } |
| ctx->SendUnregisterService(instance_name, service_type); |
| return ADBD_AUTH_UNREGISTER_OK; |
| } |