| // |
| // Copyright (C) 2012 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. |
| // |
| |
| #include "shill/vpn/vpn_provider.h" |
| |
| #include <pwd.h> |
| #include <sys/types.h> |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include <base/strings/string_util.h> |
| #include <chromeos/dbus/service_constants.h> |
| |
| #include "shill/error.h" |
| #include "shill/logging.h" |
| #include "shill/manager.h" |
| #include "shill/process_manager.h" |
| #include "shill/profile.h" |
| #include "shill/store_interface.h" |
| #include "shill/vpn/arc_vpn_driver.h" |
| #include "shill/vpn/l2tp_ipsec_driver.h" |
| #include "shill/vpn/openvpn_driver.h" |
| #include "shill/vpn/third_party_vpn_driver.h" |
| #include "shill/vpn/vpn_service.h" |
| |
| using std::string; |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kVPN; |
| static string ObjectID(const VPNProvider* v) { return "(vpn_provider)"; } |
| } |
| |
| namespace { |
| // For VPN drivers that only want to pass traffic for specific users, |
| // these are the usernames that will be used to create the routing policy |
| // rules. |
| const char* const kAllowedUsernames[] = {"chronos", "debugd"}; |
| } // namespace |
| |
| VPNProvider::VPNProvider(ControlInterface* control_interface, |
| EventDispatcher* dispatcher, |
| Metrics* metrics, |
| Manager* manager) |
| : control_interface_(control_interface), |
| dispatcher_(dispatcher), |
| metrics_(metrics), |
| manager_(manager) {} |
| |
| VPNProvider::~VPNProvider() {} |
| |
| void VPNProvider::Start() { |
| for (const auto& username : kAllowedUsernames) { |
| struct passwd* entry = getpwnam(username); |
| if (!entry) { |
| LOG(WARNING) << "Unable to look up UID for " << username << ", skipping"; |
| } else { |
| allowed_uids_.push_back(static_cast<uint32_t>(entry->pw_uid)); |
| } |
| } |
| } |
| |
| void VPNProvider::Stop() {} |
| |
| // static |
| bool VPNProvider::GetServiceParametersFromArgs(const KeyValueStore& args, |
| string* type_ptr, |
| string* name_ptr, |
| string* host_ptr, |
| Error* error) { |
| SLOG(nullptr, 2) << __func__; |
| string type = args.LookupString(kProviderTypeProperty, ""); |
| if (type.empty()) { |
| Error::PopulateAndLog( |
| FROM_HERE, error, Error::kNotSupported, "Missing VPN type property."); |
| return false; |
| } |
| |
| string host = args.LookupString(kProviderHostProperty, ""); |
| if (host.empty()) { |
| Error::PopulateAndLog( |
| FROM_HERE, error, Error::kNotSupported, "Missing VPN host property."); |
| return false; |
| } |
| |
| *type_ptr = type, |
| *host_ptr = host, |
| *name_ptr = args.LookupString(kNameProperty, ""); |
| |
| return true; |
| } |
| |
| // static |
| bool VPNProvider::GetServiceParametersFromStorage(const StoreInterface* storage, |
| const string& entry_name, |
| string* vpn_type_ptr, |
| string* name_ptr, |
| string* host_ptr, |
| Error* error) { |
| string service_type; |
| if (!storage->GetString(entry_name, kTypeProperty, &service_type) || |
| service_type != kTypeVPN) { |
| Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, |
| "Unspecified or invalid network type"); |
| return false; |
| } |
| if (!storage->GetString(entry_name, kProviderTypeProperty, vpn_type_ptr) || |
| vpn_type_ptr->empty()) { |
| Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, |
| "VPN type not specified"); |
| return false; |
| } |
| if (!storage->GetString(entry_name, kNameProperty, name_ptr) || |
| name_ptr->empty()) { |
| Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, |
| "Network name not specified"); |
| return false; |
| } |
| if (!storage->GetString(entry_name, kProviderHostProperty, host_ptr) || |
| host_ptr->empty()) { |
| Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, |
| "Host not specified"); |
| return false; |
| } |
| return true; |
| } |
| |
| ServiceRefPtr VPNProvider::GetService(const KeyValueStore& args, |
| Error* error) { |
| SLOG(this, 2) << __func__; |
| string type; |
| string name; |
| string host; |
| |
| if (!GetServiceParametersFromArgs(args, &type, &name, &host, error)) { |
| return nullptr; |
| } |
| |
| string storage_id = VPNService::CreateStorageIdentifier(args, error); |
| if (storage_id.empty()) { |
| return nullptr; |
| } |
| |
| // Find a service in the provider list which matches these parameters. |
| VPNServiceRefPtr service = FindService(type, name, host); |
| if (service == nullptr) { |
| service = CreateService(type, name, storage_id, error); |
| } |
| return service; |
| } |
| |
| ServiceRefPtr VPNProvider::FindSimilarService(const KeyValueStore& args, |
| Error* error) const { |
| SLOG(this, 2) << __func__; |
| string type; |
| string name; |
| string host; |
| |
| if (!GetServiceParametersFromArgs(args, &type, &name, &host, error)) { |
| return nullptr; |
| } |
| |
| // Find a service in the provider list which matches these parameters. |
| VPNServiceRefPtr service = FindService(type, name, host); |
| if (!service) { |
| error->Populate(Error::kNotFound, "Matching service was not found"); |
| } |
| |
| return service; |
| } |
| |
| bool VPNProvider::OnDeviceInfoAvailable(const string& link_name, |
| int interface_index, |
| Technology::Identifier technology) { |
| if (technology == Technology::kArc) { |
| arc_device_ = new VirtualDevice(control_interface_, |
| dispatcher_, |
| metrics_, |
| manager_, |
| link_name, |
| interface_index, |
| Technology::kArc); |
| arc_device_->SetFixedIpParams(true); |
| // Forward ARC->internet traffic over third-party VPN services. |
| allowed_iifs_.push_back(link_name); |
| return true; |
| } |
| |
| for (const auto& service : services_) { |
| if (service->driver()->ClaimInterface(link_name, interface_index)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void VPNProvider::RemoveService(VPNServiceRefPtr service) { |
| const auto it = std::find(services_.begin(), services_.end(), service); |
| if (it != services_.end()) { |
| services_.erase(it); |
| } |
| } |
| |
| void VPNProvider::CreateServicesFromProfile(const ProfileRefPtr& profile) { |
| SLOG(this, 2) << __func__; |
| const StoreInterface* storage = profile->GetConstStorage(); |
| KeyValueStore args; |
| args.SetString(kTypeProperty, kTypeVPN); |
| for (const auto& group : storage->GetGroupsWithProperties(args)) { |
| string type; |
| string name; |
| string host; |
| if (!GetServiceParametersFromStorage(storage, |
| group, |
| &type, |
| &name, |
| &host, |
| nullptr)) { |
| continue; |
| } |
| |
| VPNServiceRefPtr service = FindService(type, name, host); |
| if (service != nullptr) { |
| // If the service already exists, it does not need to be configured, |
| // since PushProfile would have already called ConfigureService on it. |
| SLOG(this, 2) << "Service already exists " << group; |
| continue; |
| } |
| |
| Error error; |
| service = CreateService(type, name, group, &error); |
| |
| if (service == nullptr) { |
| LOG(ERROR) << "Could not create service for " << group; |
| continue; |
| } |
| |
| if (!profile->ConfigureService(service)) { |
| LOG(ERROR) << "Could not configure service for " << group; |
| continue; |
| } |
| } |
| } |
| |
| VPNServiceRefPtr VPNProvider::CreateServiceInner(const string& type, |
| const string& name, |
| const string& storage_id, |
| Error* error) { |
| SLOG(this, 2) << __func__ << " type " << type << " name " << name |
| << " storage id " << storage_id; |
| #if defined(DISABLE_VPN) |
| |
| Error::PopulateAndLog( |
| FROM_HERE, error, Error::kNotSupported, "VPN is not supported."); |
| return nullptr; |
| |
| #else |
| |
| std::unique_ptr<VPNDriver> driver; |
| if (type == kProviderOpenVpn) { |
| driver.reset(new OpenVPNDriver( |
| control_interface_, dispatcher_, metrics_, manager_, |
| manager_->device_info(), ProcessManager::GetInstance())); |
| } else if (type == kProviderL2tpIpsec) { |
| driver.reset(new L2TPIPSecDriver( |
| control_interface_, dispatcher_, metrics_, manager_, |
| manager_->device_info(), ProcessManager::GetInstance())); |
| } else if (type == kProviderThirdPartyVpn) { |
| // For third party VPN host contains extension ID |
| driver.reset(new ThirdPartyVpnDriver( |
| control_interface_, dispatcher_, metrics_, manager_, |
| manager_->device_info())); |
| } else if (type == kProviderArcVpn) { |
| driver.reset(new ArcVpnDriver(control_interface_, |
| dispatcher_, |
| metrics_, |
| manager_, |
| manager_->device_info())); |
| } else { |
| Error::PopulateAndLog( |
| FROM_HERE, error, Error::kNotSupported, |
| "Unsupported VPN type: " + type); |
| return nullptr; |
| } |
| |
| VPNServiceRefPtr service = new VPNService( |
| control_interface_, dispatcher_, metrics_, manager_, driver.release()); |
| service->set_storage_id(storage_id); |
| service->InitDriverPropertyStore(); |
| if (!name.empty()) { |
| service->set_friendly_name(name); |
| } |
| return service; |
| |
| #endif // DISABLE_VPN |
| } |
| |
| VPNServiceRefPtr VPNProvider::CreateService(const string& type, |
| const string& name, |
| const string& storage_id, |
| Error* error) { |
| VPNServiceRefPtr service = CreateServiceInner(type, name, storage_id, error); |
| if (service) { |
| services_.push_back(service); |
| manager_->RegisterService(service); |
| } |
| |
| return service; |
| } |
| |
| VPNServiceRefPtr VPNProvider::FindService(const string& type, |
| const string& name, |
| const string& host) const { |
| for (const auto& service : services_) { |
| if (type == service->driver()->GetProviderType() && |
| name == service->friendly_name() && |
| host == service->driver()->GetHost()) { |
| return service; |
| } |
| } |
| return nullptr; |
| } |
| |
| ServiceRefPtr VPNProvider::CreateTemporaryService( |
| const KeyValueStore& args, Error* error) { |
| string type; |
| string name; |
| string host; |
| |
| if (!GetServiceParametersFromArgs(args, &type, &name, &host, error)) { |
| return nullptr; |
| } |
| |
| string storage_id = VPNService::CreateStorageIdentifier(args, error); |
| if (storage_id.empty()) { |
| return nullptr; |
| } |
| |
| return CreateServiceInner(type, name, storage_id, error); |
| } |
| |
| ServiceRefPtr VPNProvider::CreateTemporaryServiceFromProfile( |
| const ProfileRefPtr& profile, const std::string& entry_name, Error* error) { |
| string type; |
| string name; |
| string host; |
| if (!GetServiceParametersFromStorage(profile->GetConstStorage(), |
| entry_name, |
| &type, |
| &name, |
| &host, |
| error)) { |
| return nullptr; |
| } |
| |
| return CreateServiceInner(type, name, entry_name, error); |
| } |
| |
| bool VPNProvider::HasActiveService() const { |
| for (const auto& service : services_) { |
| if (service->IsConnecting() || service->IsConnected()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void VPNProvider::DisconnectAll() { |
| for (const auto& service : services_) { |
| if (service->IsConnecting() || service->IsConnected()) { |
| service->Disconnect(nullptr, "user selected new config"); |
| } |
| } |
| } |
| |
| void VPNProvider::SetDefaultRoutingPolicy(IPConfig::Properties* properties) { |
| CHECK(!allowed_uids_.empty()); |
| properties->allowed_uids = allowed_uids_; |
| properties->allowed_iifs = allowed_iifs_; |
| } |
| |
| } // namespace shill |