/* * Copyright 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * Authors: * Charles Kerr */ #include #include #include #include #include #include #include #include #include class UsbManager::Impl { public: explicit Impl( const std::string& socket_path, const std::string& public_keys_filename, const std::shared_ptr& usb_monitor, const std::shared_ptr& greeter ): m_socket_path{socket_path}, m_public_keys_filename{public_keys_filename}, m_usb_monitor{usb_monitor}, m_greeter{greeter} { m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) { m_req.reset(); }); m_greeter->state().changed().connect([this](const Greeter::State& state) { if (state == Greeter::State::INACTIVE) { maybe_snap(); } else { stop_snap(); } }); // create a new adbd client m_adbd_client.reset(new GAdbdClient{m_socket_path}); m_adbd_client->on_pk_request().connect( [this](const AdbdClient::PKRequest& req) { g_debug("%s got pk request: %s, calling maybe_snap()", G_STRLOC, req.fingerprint.c_str()); m_response = AdbdClient::PKResponse::DENY; // set the fallback response m_req.reset( new AdbdClient::PKRequest(req), [this](AdbdClient::PKRequest* r) { stop_snap(); r->respond(m_response); delete r; } ); maybe_snap(); } ); } ~Impl() { if (m_request_complete_idle_tag) { g_source_remove(m_request_complete_idle_tag); } } private: void stop_snap() { m_snap_connections.clear(); m_snap.reset(); } void maybe_snap() { // only prompt if there's something to prompt about if (!m_req) { return; } // only prompt in an unlocked session if (m_greeter->state().get() != Greeter::State::INACTIVE) { return; } snap(); } void snap() { m_snap = std::make_shared(m_req->fingerprint); m_snap_connections.insert((*m_snap).on_user_response().connect( [this](AdbdClient::PKResponse response, bool remember_choice){ if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) { write_public_key(m_req->public_key); } m_response = response; // defer finishing the request into an idle func because // ScopedConnections can't be destroyed inside their callbacks if (m_request_complete_idle_tag == 0) { m_request_complete_idle_tag = g_idle_add([](gpointer gself){ auto self = static_cast(gself); self->m_request_complete_idle_tag = 0; self->m_req.reset(); return G_SOURCE_REMOVE; }, this); } } )); } void write_public_key(const std::string& public_key) { g_debug("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); // confirm the directory exists auto dirname = g_path_get_dirname(m_public_keys_filename.c_str()); const auto dir_exists = g_file_test(dirname, G_FILE_TEST_IS_DIR); if (!dir_exists) g_warning("ADB data directory '%s' does not exist", dirname); g_clear_pointer(&dirname, g_free); if (!dir_exists) return; // open the file in append mode, with user rw and group r permissions const auto fd = open( m_public_keys_filename.c_str(), O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP ); if (fd == -1) { g_warning("Error opening ADB datafile: %s", g_strerror(errno)); return; } // write the new public key on its own line std::string buf {public_key + '\n'}; if (write(fd, buf.c_str(), buf.size()) == -1) g_warning("Error writing ADB datafile: %d %s", errno, g_strerror(errno)); close(fd); } const std::string m_socket_path; const std::string m_public_keys_filename; const std::shared_ptr m_usb_monitor; const std::shared_ptr m_greeter; unsigned int m_request_complete_idle_tag {}; std::shared_ptr m_adbd_client; std::shared_ptr m_snap; std::set m_snap_connections; AdbdClient::PKResponse m_response {AdbdClient::PKResponse::DENY}; std::shared_ptr m_req; }; /*** **** ***/ UsbManager::UsbManager( const std::string& socket_path, const std::string& public_keys_filename, const std::shared_ptr& usb_monitor, const std::shared_ptr& greeter ): impl{new Impl{socket_path, public_keys_filename, usb_monitor, greeter}} { } UsbManager::~UsbManager() { }