| // 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 "webserver/webservd/server.h" |
| |
| #include <openssl/evp.h> |
| #include <openssl/x509.h> |
| |
| #include <limits> |
| |
| #include <base/rand_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <chromeos/dbus/async_event_sequencer.h> |
| |
| #include "webserver/webservd/dbus_protocol_handler.h" |
| #include "webserver/webservd/protocol_handler.h" |
| #include "webserver/webservd/utils.h" |
| |
| using chromeos::dbus_utils::AsyncEventSequencer; |
| using chromeos::dbus_utils::DBusObject; |
| using chromeos::dbus_utils::ExportedObjectManager; |
| |
| namespace { |
| |
| void OnFirewallSuccess(const std::string& itf_name, |
| uint16_t port, |
| bool allowed) { |
| if (allowed) { |
| LOG(INFO) << "Successfully opened up port " << port << " on interface " |
| << itf_name; |
| } else { |
| LOG(ERROR) << "Failed to open up port " << port << ", interface: " |
| << itf_name; |
| } |
| } |
| |
| void IgnoreFirewallDBusMethodError(chromeos::Error* error) { |
| } |
| |
| } // anonymous namespace |
| |
| namespace webservd { |
| |
| Server::Server(ExportedObjectManager* object_manager, const Config& config) |
| : dbus_object_{new DBusObject{ |
| object_manager, object_manager->GetBus(), |
| org::chromium::WebServer::ServerAdaptor::GetObjectPath()}}, |
| config_{config} { |
| int fds[2]; |
| PCHECK(pipe(fds) == 0) << "Failed to create firewall lifeline pipe"; |
| lifeline_read_fd_ = fds[0]; |
| lifeline_write_fd_ = fds[1]; |
| } |
| |
| Server::~Server() { |
| close(lifeline_read_fd_); |
| close(lifeline_write_fd_); |
| } |
| |
| void Server::RegisterAsync( |
| const AsyncEventSequencer::CompletionAction& completion_callback) { |
| scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer()); |
| dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get()); |
| |
| InitTlsData(); |
| |
| for (auto& handler_config : config_.protocol_handlers) |
| CreateProtocolHandler(&handler_config); |
| |
| permission_broker_object_manager_.reset( |
| new org::chromium::PermissionBroker::ObjectManagerProxy{ |
| dbus_object_->GetBus()}); |
| permission_broker_object_manager_->SetPermissionBrokerAddedCallback( |
| base::Bind(&Server::OnPermissionBrokerOnline, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| dbus_object_->RegisterAsync( |
| sequencer->GetHandler("Failed exporting Server.", true)); |
| |
| for (const auto& pair : protocol_handler_map_) { |
| pair.second->RegisterAsync( |
| sequencer->GetHandler("Failed exporting ProtocolHandler.", false)); |
| } |
| sequencer->OnAllTasksCompletedCall({completion_callback}); |
| } |
| |
| void Server::OnPermissionBrokerOnline( |
| org::chromium::PermissionBrokerProxy* proxy) { |
| LOG(INFO) << "Permission broker is on-line. " |
| << "Opening firewall for protocol handlers"; |
| dbus::FileDescriptor dbus_fd{lifeline_read_fd_}; |
| dbus_fd.CheckValidity(); |
| for (auto& handler_config : config_.protocol_handlers) { |
| VLOG(1) << "Firewall request: Protocol Handler = " << handler_config.name |
| << ", Port = " << handler_config.port << ", Interface = " |
| << handler_config.interface_name; |
| proxy->RequestTcpPortAccessAsync( |
| handler_config.port, |
| handler_config.interface_name, |
| dbus_fd, |
| base::Bind(&OnFirewallSuccess, handler_config.interface_name, |
| handler_config.port), |
| base::Bind(&IgnoreFirewallDBusMethodError)); |
| } |
| } |
| |
| std::string Server::Ping() { |
| return "Web Server is running"; |
| } |
| |
| void Server::ProtocolHandlerStarted(ProtocolHandler* handler) { |
| CHECK(protocol_handler_map_.find(handler) == protocol_handler_map_.end()) |
| << "Protocol handler already registered"; |
| std::string path = base::StringPrintf("/org/chromium/WebServer/Servers/%d", |
| ++last_protocol_handler_index_); |
| dbus::ObjectPath object_path{path}; |
| std::unique_ptr<DBusProtocolHandler> dbus_protocol_handler{ |
| new DBusProtocolHandler{dbus_object_->GetObjectManager().get(), |
| object_path, |
| handler, |
| this} |
| }; |
| protocol_handler_map_.emplace(handler, std::move(dbus_protocol_handler)); |
| } |
| |
| void Server::ProtocolHandlerStopped(ProtocolHandler* handler) { |
| CHECK_EQ(1u, protocol_handler_map_.erase(handler)) |
| << "Unknown protocol handler"; |
| } |
| |
| void Server::CreateProtocolHandler(Config::ProtocolHandler* handler_config) { |
| std::unique_ptr<ProtocolHandler> protocol_handler{ |
| new ProtocolHandler{handler_config->name, this}}; |
| if (protocol_handler->Start(handler_config)) |
| protocol_handlers_.push_back(std::move(protocol_handler)); |
| } |
| |
| void Server::InitTlsData() { |
| if (!TLS_certificate_.empty()) |
| return; // Already initialized. |
| |
| // TODO(avakulenko): verify these constants and provide sensible values |
| // for the long-term. See brbug.com/227 |
| const int kKeyLengthBits = 1024; |
| const base::TimeDelta kCertExpiration = base::TimeDelta::FromDays(365); |
| const char kCommonName[] = "Brillo device"; |
| |
| // Create the X509 certificate. |
| int cert_serial_number = base::RandInt(0, std::numeric_limits<int>::max()); |
| auto cert = |
| CreateCertificate(cert_serial_number, kCertExpiration, kCommonName); |
| |
| // Create RSA key pair. |
| auto rsa_key_pair = GenerateRSAKeyPair(kKeyLengthBits); |
| |
| // Store the private key to a temp buffer. |
| // Do not assign it to |TLS_private_key_| yet until the end when we are sure |
| // everything else has worked out. |
| chromeos::SecureBlob private_key = StoreRSAPrivateKey(rsa_key_pair.get()); |
| |
| // Create EVP key and set it to the certificate. |
| auto key = std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)>{EVP_PKEY_new(), |
| EVP_PKEY_free}; |
| CHECK(key.get()); |
| // Transfer ownership of |rsa_key_pair| to |key|. |
| CHECK(EVP_PKEY_assign_RSA(key.get(), rsa_key_pair.release())); |
| CHECK(X509_set_pubkey(cert.get(), key.get())); |
| |
| // Sign the certificate. |
| CHECK(X509_sign(cert.get(), key.get(), EVP_sha256())); |
| |
| TLS_certificate_ = StoreCertificate(cert.get()); |
| TLS_certificate_fingerprint_ = GetSha256Fingerprint(cert.get()); |
| TLS_private_key_ = std::move(private_key); |
| |
| // Update the TLS data in protocol handler config. |
| for (auto& handler_config : config_.protocol_handlers) { |
| if (handler_config.use_tls) { |
| handler_config.certificate = TLS_certificate_; |
| handler_config.certificate_fingerprint = TLS_certificate_fingerprint_; |
| handler_config.private_key = TLS_private_key_; |
| } |
| } |
| } |
| |
| } // namespace webservd |