| // Copyright 2015 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "lorgnette/firewall_manager.h" |
| |
| #include <base/bind.h> |
| #include <brillo/errors/error.h> |
| |
| using std::string; |
| |
| namespace lorgnette { |
| |
| namespace { |
| |
| const uint16_t kCanonBjnpPort = 8612; |
| const int kInvalidFd = -1; |
| |
| } // namespace |
| |
| FirewallManager::FirewallManager(const std::string& interface) |
| : lifeline_read_fd_(kInvalidFd), |
| lifeline_write_fd_(kInvalidFd), |
| interface_(interface) {} |
| |
| FirewallManager::~FirewallManager() { |
| if (lifeline_read_fd_ != kInvalidFd) { |
| close(lifeline_read_fd_); |
| close(lifeline_write_fd_); |
| } |
| } |
| |
| void FirewallManager::Init(const scoped_refptr<dbus::Bus>& bus) { |
| CHECK(!permission_broker_proxy_) << "Already started"; |
| |
| if (!SetupLifelinePipe()) { |
| return; |
| } |
| |
| if (!bus) { |
| LOG(ERROR) << "Bus is null; assuming we are in tests."; |
| return; |
| } |
| |
| permission_broker_proxy_.reset(new org::chromium::PermissionBrokerProxy(bus)); |
| |
| // This will connect the name owner changed signal in DBus object proxy, |
| // The callback will be invoked as soon as service is avalilable and will |
| // be cleared after it is invoked. So this will be an one time callback. |
| permission_broker_proxy_->GetObjectProxy()->WaitForServiceToBeAvailable( |
| base::Bind(&FirewallManager::OnServiceAvailable, base::Unretained(this))); |
| |
| // This will continuously monitor the name owner of the service. However, |
| // it does not connect the name owner changed signal in DBus object proxy |
| // for some reason. In order to connect the name owner changed signal, |
| // either WaitForServiceToBeAvaiable or ConnectToSignal need to be invoked. |
| // Since we're not interested in any signals from the proxy, |
| // WaitForServiceToBeAvailable is used. |
| permission_broker_proxy_->GetObjectProxy()->SetNameOwnerChangedCallback( |
| base::Bind(&FirewallManager::OnServiceNameChanged, |
| base::Unretained(this))); |
| } |
| |
| void FirewallManager::RequestScannerPortAccess() { |
| // Request access for all well-known ports that the scanimage process will |
| // listen to. |
| RequestUdpPortAccess(kCanonBjnpPort); |
| } |
| |
| bool FirewallManager::SetupLifelinePipe() { |
| if (lifeline_read_fd_ != kInvalidFd) { |
| LOG(ERROR) << "Lifeline pipe already created"; |
| return false; |
| } |
| |
| // Setup lifeline pipe. |
| int fds[2]; |
| if (pipe(fds) != 0) { |
| PLOG(ERROR) << "Failed to create lifeline pipe"; |
| return false; |
| } |
| lifeline_read_fd_ = fds[0]; |
| lifeline_write_fd_ = fds[1]; |
| |
| return true; |
| } |
| |
| void FirewallManager::OnServiceAvailable(bool service_available) { |
| LOG(INFO) << "FirewallManager::OnServiceAvailabe " << service_available; |
| // Nothing to be done if proxy service is not available. |
| if (!service_available) { |
| return; |
| } |
| RequestAllPortsAccess(); |
| } |
| |
| void FirewallManager::OnServiceNameChanged(const string& old_owner, |
| const string& new_owner) { |
| LOG(INFO) << "FirewallManager::OnServiceNameChanged old " << old_owner |
| << " new " << new_owner; |
| // Nothing to be done if no owner is attached to the proxy service. |
| if (new_owner.empty()) { |
| return; |
| } |
| RequestAllPortsAccess(); |
| } |
| |
| void FirewallManager::RequestAllPortsAccess() { |
| std::set<uint16_t> attempted_ports; |
| attempted_ports.swap(requested_ports_); |
| for (const auto& port : attempted_ports) { |
| RequestUdpPortAccess(port); |
| } |
| } |
| |
| void FirewallManager::ReleaseAllPortsAccess() { |
| for (const auto& port : requested_ports_) { |
| ReleaseUdpPortAccess(port); |
| } |
| } |
| |
| void FirewallManager::RequestUdpPortAccess(uint16_t port) { |
| if (!permission_broker_proxy_) { |
| LOG(INFO) << "Permission broker does not exist (yet); adding request for " |
| << "port " << port << " to queue."; |
| requested_ports_.insert(port); |
| return; |
| } |
| |
| bool allowed = false; |
| // Pass the read end of the pipe to permission_broker, for it to monitor this |
| // process. |
| brillo::ErrorPtr error; |
| if (!permission_broker_proxy_->RequestUdpPortAccess(port, |
| interface_, |
| lifeline_read_fd_, |
| &allowed, |
| &error)) { |
| LOG(ERROR) << "Failed to request UDP port access: " |
| << error->GetCode() << " " << error->GetMessage(); |
| return; |
| } |
| if (!allowed) { |
| LOG(ERROR) << "Access request for UDP port " << port |
| << " on interface " << interface_ << " is denied"; |
| return; |
| } |
| LOG(INFO) << "Access granted for UDP port " << port |
| << " on interface " << interface_; |
| requested_ports_.insert(port); |
| } |
| |
| void FirewallManager::ReleaseUdpPortAccess(uint16_t port) { |
| brillo::ErrorPtr error; |
| bool success; |
| if (requested_ports_.find(port) == requested_ports_.end()) { |
| LOG(ERROR) << "UDP access has not been requested for port: " |
| << port; |
| return; |
| } |
| if (!permission_broker_proxy_) { |
| requested_ports_.erase(port); |
| return; |
| } |
| |
| if (!permission_broker_proxy_->ReleaseUdpPort(port, |
| interface_, |
| &success, |
| &error)) { |
| LOG(ERROR) << "Failed to release UDP port access: " |
| << error->GetCode() << " " << error->GetMessage(); |
| return; |
| } |
| if (!success) { |
| LOG(ERROR) << "Release request for UDP port " << port |
| << " on interface " << interface_ << " is denied"; |
| return; |
| } |
| LOG(INFO) << "Access released for UDP port " << port |
| << " on interface " << interface_; |
| requested_ports_.erase(port); |
| } |
| |
| } // namespace lorgnette |