// // 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/openvpn_driver.h" #include <algorithm> #include <base/files/file_path.h> #include <base/files/file_util.h> #include <base/files/scoped_temp_dir.h> #include <base/strings/string_util.h> #include <base/strings/stringprintf.h> #if defined(__ANDROID__) #include <dbus/service_constants.h> #else #include <chromeos/dbus/service_constants.h> #endif // __ANDROID__ #include <gtest/gtest.h> #include "shill/error.h" #include "shill/ipconfig.h" #include "shill/logging.h" #include "shill/mock_adaptors.h" #include "shill/mock_certificate_file.h" #include "shill/mock_device_info.h" #include "shill/mock_event_dispatcher.h" #include "shill/mock_manager.h" #include "shill/mock_metrics.h" #include "shill/mock_process_manager.h" #include "shill/mock_service.h" #include "shill/mock_store.h" #include "shill/mock_virtual_device.h" #include "shill/nice_mock_control.h" #include "shill/rpc_task.h" #include "shill/technology.h" #include "shill/virtual_device.h" #include "shill/vpn/mock_openvpn_management_server.h" #include "shill/vpn/mock_vpn_service.h" #include "shill/vpn/vpn_service.h" using base::FilePath; using base::WeakPtr; using std::map; using std::string; using std::vector; using testing::_; using testing::AnyNumber; using testing::DoAll; using testing::ElementsAreArray; using testing::Field; using testing::Mock; using testing::Ne; using testing::NiceMock; using testing::Return; using testing::SetArgumentPointee; using testing::StrictMock; namespace shill { struct AuthenticationExpectations { AuthenticationExpectations() : remote_authentication_type(Metrics::kVpnRemoteAuthenticationTypeMax) {} AuthenticationExpectations( const string& ca_cert_in, const string& client_cert_in, const string& user_in, const string& otp_in, const string& token_in, Metrics::VpnRemoteAuthenticationType remote_authentication_type_in, const vector<Metrics::VpnUserAuthenticationType> &user_authentication_types_in) : ca_cert(ca_cert_in), client_cert(client_cert_in), user(user_in), otp(otp_in), token(token_in), remote_authentication_type(remote_authentication_type_in), user_authentication_types(user_authentication_types_in) {} string ca_cert; string client_cert; string user; string otp; string token; Metrics::VpnRemoteAuthenticationType remote_authentication_type; vector<Metrics::VpnUserAuthenticationType> user_authentication_types; }; class OpenVPNDriverTest : public testing::TestWithParam<AuthenticationExpectations>, public RPCTaskDelegate { public: OpenVPNDriverTest() : device_info_(&control_, &dispatcher_, &metrics_, &manager_), metrics_(&dispatcher_), manager_(&control_, &dispatcher_, &metrics_), driver_(new OpenVPNDriver(&control_, &dispatcher_, &metrics_, &manager_, &device_info_, &process_manager_)), service_(new MockVPNService(&control_, &dispatcher_, &metrics_, &manager_, driver_)), device_(new MockVirtualDevice( &control_, &dispatcher_, &metrics_, &manager_, kInterfaceName, kInterfaceIndex, Technology::kVPN)), certificate_file_(new MockCertificateFile()), extra_certificates_file_(new MockCertificateFile()), management_server_(new NiceMock<MockOpenVPNManagementServer>()) { driver_->management_server_.reset(management_server_); driver_->certificate_file_.reset(certificate_file_); // Passes ownership. driver_->extra_certificates_file_.reset( extra_certificates_file_); // Passes ownership. CHECK(temporary_directory_.CreateUniqueTempDir()); driver_->openvpn_config_directory_ = temporary_directory_.path().Append(kOpenVPNConfigDirectory); } virtual ~OpenVPNDriverTest() {} virtual void TearDown() { driver_->default_service_callback_tag_ = 0; driver_->pid_ = 0; driver_->device_ = nullptr; driver_->service_ = nullptr; if (!lsb_release_file_.empty()) { EXPECT_TRUE(base::DeleteFile(lsb_release_file_, false)); lsb_release_file_.clear(); } } protected: static const char kOption[]; static const char kProperty[]; static const char kValue[]; static const char kOption2[]; static const char kProperty2[]; static const char kValue2[]; static const char kGateway1[]; static const char kNetmask1[]; static const char kNetwork1[]; static const char kGateway2[]; static const char kNetmask2[]; static const char kNetwork2[]; static const char kInterfaceName[]; static const int kInterfaceIndex; static const char kOpenVPNConfigDirectory[]; void SetArg(const string& arg, const string& value) { driver_->args()->SetString(arg, value); } void SetArgArray(const string& arg, const vector<string>& value) { driver_->args()->SetStrings(arg, value); } KeyValueStore* GetArgs() { return driver_->args(); } KeyValueStore GetProviderProperties(const PropertyStore& store) { KeyValueStore props; Error error; EXPECT_TRUE( store.GetKeyValueStoreProperty(kProviderProperty, &props, &error)); return props; } void RemoveStringArg(const string& arg) { driver_->args()->RemoveString(arg); } const ServiceRefPtr& GetSelectedService() { return device_->selected_service(); } bool InitManagementChannelOptions( vector<vector<string>>* options, Error* error) { return driver_->InitManagementChannelOptions(options, error); } Sockets* GetSockets() { return &driver_->sockets_; } void SetDevice(const VirtualDeviceRefPtr& device) { driver_->device_ = device; } void SetService(const VPNServiceRefPtr& service) { driver_->service_ = service; } VPNServiceRefPtr GetService() { return driver_->service_; } void OnConnectionDisconnected() { driver_->OnConnectionDisconnected(); } void OnConnectTimeout() { driver_->OnConnectTimeout(); } void StartConnectTimeout(int timeout_seconds) { driver_->StartConnectTimeout(timeout_seconds); } bool IsConnectTimeoutStarted() { return driver_->IsConnectTimeoutStarted(); } static int GetDefaultConnectTimeoutSeconds() { return OpenVPNDriver::kDefaultConnectTimeoutSeconds; } static int GetReconnectOfflineTimeoutSeconds() { return OpenVPNDriver::kReconnectOfflineTimeoutSeconds; } static int GetReconnectTLSErrorTimeoutSeconds() { return OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds; } static int GetReconnectTimeoutSeconds(OpenVPNDriver::ReconnectReason reason) { return OpenVPNDriver::GetReconnectTimeoutSeconds(reason); } void SetClientState(const string& state) { management_server_->state_ = state; } // Used to assert that a flag appears in the options. void ExpectInFlags(const vector<vector<string>>& options, const string& flag); void ExpectInFlags(const vector<vector<string>>& options, const string& flag, const string& value); void ExpectInFlags(const vector<vector<string>>& options, const vector<string>& arguments); void ExpectNotInFlags(const vector<vector<string>>& options, const string& flag); void SetupLSBRelease(); // Inherited from RPCTaskDelegate. virtual void GetLogin(string* user, string* password); virtual void Notify(const string& reason, const map<string, string>& dict); NiceMockControl control_; NiceMock<MockDeviceInfo> device_info_; MockEventDispatcher dispatcher_; MockMetrics metrics_; MockProcessManager process_manager_; MockManager manager_; OpenVPNDriver* driver_; // Owned by |service_|. scoped_refptr<MockVPNService> service_; scoped_refptr<MockVirtualDevice> device_; MockCertificateFile* certificate_file_; // Owned by |driver_|. MockCertificateFile* extra_certificates_file_; // Owned by |driver_|. base::ScopedTempDir temporary_directory_; // Owned by |driver_|. NiceMock<MockOpenVPNManagementServer>* management_server_; FilePath lsb_release_file_; }; const char OpenVPNDriverTest::kOption[] = "openvpn-option"; const char OpenVPNDriverTest::kProperty[] = "OpenVPN.SomeProperty"; const char OpenVPNDriverTest::kValue[] = "some-property-value"; const char OpenVPNDriverTest::kOption2[] = "openvpn-option2"; const char OpenVPNDriverTest::kProperty2[] = "OpenVPN.SomeProperty2"; const char OpenVPNDriverTest::kValue2[] = "some-property-value2"; const char OpenVPNDriverTest::kGateway1[] = "10.242.2.13"; const char OpenVPNDriverTest::kNetmask1[] = "255.255.255.255"; const char OpenVPNDriverTest::kNetwork1[] = "10.242.2.1"; const char OpenVPNDriverTest::kGateway2[] = "10.242.2.14"; const char OpenVPNDriverTest::kNetmask2[] = "255.255.0.0"; const char OpenVPNDriverTest::kNetwork2[] = "192.168.0.0"; const char OpenVPNDriverTest::kInterfaceName[] = "tun0"; const int OpenVPNDriverTest::kInterfaceIndex = 123; const char OpenVPNDriverTest::kOpenVPNConfigDirectory[] = "openvpn"; void OpenVPNDriverTest::GetLogin(string* /*user*/, string* /*password*/) {} void OpenVPNDriverTest::Notify(const string& /*reason*/, const map<string, string>& /*dict*/) {} void OpenVPNDriverTest::ExpectInFlags(const vector<vector<string>>& options, const string& flag) { ExpectInFlags(options, vector<string> { flag }); } void OpenVPNDriverTest::ExpectInFlags(const vector<vector<string>>& options, const string& flag, const string& value) { ExpectInFlags(options, vector<string> { flag, value }); } void OpenVPNDriverTest::ExpectInFlags(const vector<vector<string>>& options, const vector<string>& arguments) { EXPECT_TRUE(std::find(options.begin(), options.end(), arguments) != options.end()); } void OpenVPNDriverTest::ExpectNotInFlags(const vector<vector<string>>& options, const string& flag) { for (const auto& option : options) { EXPECT_NE(flag, option[0]); } } void OpenVPNDriverTest::SetupLSBRelease() { static const char kLSBReleaseContents[] = "\n" "=\n" "foo=\n" "=bar\n" "zoo==\n" "CHROMEOS_RELEASE_BOARD=x86-alex\n" "CHROMEOS_RELEASE_NAME=Chromium OS\n" "CHROMEOS_RELEASE_VERSION=2202.0\n"; EXPECT_TRUE(base::CreateTemporaryFile(&lsb_release_file_)); EXPECT_EQ(arraysize(kLSBReleaseContents), base::WriteFile(lsb_release_file_, kLSBReleaseContents, arraysize(kLSBReleaseContents))); EXPECT_EQ(OpenVPNDriver::kLSBReleaseFile, driver_->lsb_release_file_.value()); driver_->lsb_release_file_ = lsb_release_file_; } TEST_F(OpenVPNDriverTest, Connect) { EXPECT_CALL(*service_, SetState(Service::kStateConfiguring)); const string interface = kInterfaceName; EXPECT_CALL(device_info_, CreateTunnelInterface(_)) .WillOnce(DoAll(SetArgumentPointee<0>(interface), Return(true))); Error error; driver_->Connect(service_, &error); EXPECT_TRUE(error.IsSuccess()); EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_); EXPECT_TRUE(driver_->IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, ConnectTunnelFailure) { EXPECT_CALL(*service_, SetState(Service::kStateConfiguring)); EXPECT_CALL(device_info_, CreateTunnelInterface(_)).WillOnce(Return(false)); EXPECT_CALL(*service_, SetFailure(Service::kFailureInternal)); Error error; driver_->Connect(service_, &error); EXPECT_EQ(Error::kInternalError, error.type()); EXPECT_TRUE(driver_->tunnel_interface_.empty()); EXPECT_FALSE(driver_->IsConnectTimeoutStarted()); } namespace { MATCHER_P(IsIPAddress, address, "") { IPAddress ip_address(IPAddress::kFamilyIPv4); EXPECT_TRUE(ip_address.SetAddressFromString(address)); return ip_address.Equals(arg); } } // namespace TEST_F(OpenVPNDriverTest, Notify) { map<string, string> config; driver_->service_ = service_; driver_->device_ = device_; StartConnectTimeout(0); EXPECT_CALL(*device_, UpdateIPConfig(Field(&IPConfig::Properties::address, ""))); driver_->Notify("up", config); EXPECT_FALSE(driver_->IsConnectTimeoutStarted()); EXPECT_TRUE(GetSelectedService().get() == service_.get()); // Tests that existing properties are reused if no new ones provided. driver_->ip_properties_.address = "1.2.3.4"; EXPECT_CALL(*device_, UpdateIPConfig(Field(&IPConfig::Properties::address, "1.2.3.4"))); driver_->Notify("up", config); } TEST_P(OpenVPNDriverTest, NotifyUMA) { map<string, string> config; driver_->service_ = service_; driver_->device_ = device_; // Check that UMA metrics are emitted on Notify. EXPECT_CALL(*device_, UpdateIPConfig(_)); EXPECT_CALL(metrics_, SendEnumToUMA( Metrics::kMetricVpnDriver, Metrics::kVpnDriverOpenVpn, Metrics::kMetricVpnDriverMax)); EXPECT_CALL(metrics_, SendEnumToUMA( Metrics::kMetricVpnRemoteAuthenticationType, GetParam().remote_authentication_type, Metrics::kVpnRemoteAuthenticationTypeMax)); for (const auto& authentication_type : GetParam().user_authentication_types) { EXPECT_CALL(metrics_, SendEnumToUMA( Metrics::kMetricVpnUserAuthenticationType, authentication_type, Metrics::kVpnUserAuthenticationTypeMax)); } Error unused_error; PropertyStore store; driver_->InitPropertyStore(&store); if (!GetParam().ca_cert.empty()) { store.SetStringsProperty(kOpenVPNCaCertPemProperty, vector<string>{ GetParam().ca_cert }, &unused_error); } if (!GetParam().client_cert.empty()) { store.SetStringProperty(kOpenVPNClientCertIdProperty, GetParam().client_cert, &unused_error); } if (!GetParam().user.empty()) { store.SetStringProperty(kOpenVPNUserProperty, GetParam().user, &unused_error); } if (!GetParam().otp.empty()) { store.SetStringProperty(kOpenVPNOTPProperty, GetParam().otp, &unused_error); } if (!GetParam().token.empty()) { store.SetStringProperty(kOpenVPNTokenProperty, GetParam().token, &unused_error); } driver_->Notify("up", config); Mock::VerifyAndClearExpectations(&metrics_); } INSTANTIATE_TEST_CASE_P( OpenVPNDriverAuthenticationTypes, OpenVPNDriverTest, ::testing::Values( AuthenticationExpectations( "", "", "", "", "", Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnNone }), AuthenticationExpectations( "", "client_cert", "", "", "", Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate }), AuthenticationExpectations( "", "client_cert", "user", "", "", Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword }), AuthenticationExpectations( "", "", "user", "", "", Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword }), AuthenticationExpectations( "", "client_cert", "user", "otp", "", Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp }), AuthenticationExpectations( "", "client_cert", "user", "otp", "token", Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernameToken }), AuthenticationExpectations( "ca_cert", "client_cert", "user", "otp", "token", Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate, vector<Metrics::VpnUserAuthenticationType> { Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp, Metrics::kVpnUserAuthenticationTypeOpenVpnUsernameToken }))); TEST_F(OpenVPNDriverTest, NotifyFail) { map<string, string> dict; driver_->device_ = device_; StartConnectTimeout(0); EXPECT_CALL(*device_, DropConnection()); driver_->Notify("fail", dict); EXPECT_TRUE(driver_->IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, GetRouteOptionEntry) { OpenVPNDriver::RouteOptions routes; EXPECT_EQ(nullptr, OpenVPNDriver::GetRouteOptionEntry("foo", "bar", &routes)); EXPECT_TRUE(routes.empty()); EXPECT_EQ(nullptr, OpenVPNDriver::GetRouteOptionEntry("foo", "foo", &routes)); EXPECT_TRUE(routes.empty()); EXPECT_EQ(nullptr, OpenVPNDriver::GetRouteOptionEntry("foo", "fooz", &routes)); EXPECT_TRUE(routes.empty()); IPConfig::Route* route = OpenVPNDriver::GetRouteOptionEntry("foo", "foo12", &routes); EXPECT_EQ(1, routes.size()); EXPECT_EQ(route, &routes[12]); route = OpenVPNDriver::GetRouteOptionEntry("foo", "foo13", &routes); EXPECT_EQ(2, routes.size()); EXPECT_EQ(route, &routes[13]); } TEST_F(OpenVPNDriverTest, ParseRouteOption) { OpenVPNDriver::RouteOptions routes; OpenVPNDriver::ParseRouteOption("foo", "bar", &routes); EXPECT_TRUE(routes.empty()); OpenVPNDriver::ParseRouteOption("gateway_2", kGateway2, &routes); OpenVPNDriver::ParseRouteOption("netmask_2", kNetmask2, &routes); OpenVPNDriver::ParseRouteOption("network_2", kNetwork2, &routes); EXPECT_EQ(1, routes.size()); OpenVPNDriver::ParseRouteOption("gateway_1", kGateway1, &routes); OpenVPNDriver::ParseRouteOption("netmask_1", kNetmask1, &routes); OpenVPNDriver::ParseRouteOption("network_1", kNetwork1, &routes); EXPECT_EQ(2, routes.size()); EXPECT_EQ(kGateway1, routes[1].gateway); EXPECT_EQ(kNetmask1, routes[1].netmask); EXPECT_EQ(kNetwork1, routes[1].host); EXPECT_EQ(kGateway2, routes[2].gateway); EXPECT_EQ(kNetmask2, routes[2].netmask); EXPECT_EQ(kNetwork2, routes[2].host); } TEST_F(OpenVPNDriverTest, SetRoutes) { OpenVPNDriver::RouteOptions routes; routes[1].gateway = "1.2.3.4"; routes[1].host = "1.2.3.4"; routes[2].host = "2.3.4.5"; routes[2].netmask = "255.0.0.0"; routes[3].netmask = "255.0.0.0"; routes[3].gateway = "1.2.3.5"; routes[5].host = kNetwork2; routes[5].netmask = kNetmask2; routes[5].gateway = kGateway2; routes[4].host = kNetwork1; routes[4].netmask = kNetmask1; routes[4].gateway = kGateway1; IPConfig::Properties props; OpenVPNDriver::SetRoutes(routes, &props); ASSERT_EQ(2, props.routes.size()); EXPECT_EQ(kGateway1, props.routes[0].gateway); EXPECT_EQ(kNetmask1, props.routes[0].netmask); EXPECT_EQ(kNetwork1, props.routes[0].host); EXPECT_EQ(kGateway2, props.routes[1].gateway); EXPECT_EQ(kNetmask2, props.routes[1].netmask); EXPECT_EQ(kNetwork2, props.routes[1].host); // Tests that the routes are not reset if no new routes are supplied. OpenVPNDriver::SetRoutes(OpenVPNDriver::RouteOptions(), &props); EXPECT_EQ(2, props.routes.size()); } TEST_F(OpenVPNDriverTest, SplitPortFromHost) { string name, port; EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("", nullptr, nullptr)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost(":1234", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:f:1234", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:x", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:-1", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:+1", &name, &port)); EXPECT_FALSE(OpenVPNDriver::SplitPortFromHost("v.com:65536", &name, &port)); EXPECT_TRUE(OpenVPNDriver::SplitPortFromHost("v.com:0", &name, &port)); EXPECT_EQ("v.com", name); EXPECT_EQ("0", port); EXPECT_TRUE(OpenVPNDriver::SplitPortFromHost("w.com:65535", &name, &port)); EXPECT_EQ("w.com", name); EXPECT_EQ("65535", port); EXPECT_TRUE(OpenVPNDriver::SplitPortFromHost("x.com:12345", &name, &port)); EXPECT_EQ("x.com", name); EXPECT_EQ("12345", port); } TEST_F(OpenVPNDriverTest, ParseForeignOption) { vector<string> domain_search; vector<string> dns_servers; IPConfig::Properties props; OpenVPNDriver::ParseForeignOption("", &domain_search, &dns_servers); OpenVPNDriver::ParseForeignOption( "dhcp-option DOMAIN", &domain_search, &dns_servers); OpenVPNDriver::ParseForeignOption( "dhcp-option DOMAIN zzz.com foo", &domain_search, &dns_servers); OpenVPNDriver::ParseForeignOption( "dhcp-Option DOmAIN xyz.com", &domain_search, &dns_servers); ASSERT_EQ(1, domain_search.size()); EXPECT_EQ("xyz.com", domain_search[0]); OpenVPNDriver::ParseForeignOption( "dhcp-option DnS 1.2.3.4", &domain_search, &dns_servers); ASSERT_EQ(1, dns_servers.size()); EXPECT_EQ("1.2.3.4", dns_servers[0]); } TEST_F(OpenVPNDriverTest, ParseForeignOptions) { // This also tests that std::map is a sorted container. map<int, string> options; options[5] = "dhcp-option DOMAIN five.com"; options[2] = "dhcp-option DOMAIN two.com"; options[8] = "dhcp-option DOMAIN eight.com"; options[7] = "dhcp-option DOMAIN seven.com"; options[4] = "dhcp-option DOMAIN four.com"; options[10] = "dhcp-option dns 1.2.3.4"; IPConfig::Properties props; OpenVPNDriver::ParseForeignOptions(options, &props); ASSERT_EQ(5, props.domain_search.size()); EXPECT_EQ("two.com", props.domain_search[0]); EXPECT_EQ("four.com", props.domain_search[1]); EXPECT_EQ("five.com", props.domain_search[2]); EXPECT_EQ("seven.com", props.domain_search[3]); EXPECT_EQ("eight.com", props.domain_search[4]); ASSERT_EQ(1, props.dns_servers.size()); EXPECT_EQ("1.2.3.4", props.dns_servers[0]); // Test that the DNS properties are not updated if no new DNS properties are // supplied. OpenVPNDriver::ParseForeignOptions(map<int, string>(), &props); EXPECT_EQ(5, props.domain_search.size()); ASSERT_EQ(1, props.dns_servers.size()); } TEST_F(OpenVPNDriverTest, ParseIPConfiguration) { map<string, string> config; IPConfig::Properties props; driver_->ParseIPConfiguration(config, &props); EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family); EXPECT_EQ(32, props.subnet_prefix); props.subnet_prefix = 18; driver_->ParseIPConfiguration(config, &props); EXPECT_EQ(18, props.subnet_prefix); // An "ifconfig_remote" parameter that looks like a netmask should be // applied to the subnet prefix instead of to the peer address. config["ifconfig_remotE"] = "255.255.0.0"; driver_->ParseIPConfiguration(config, &props); EXPECT_EQ(16, props.subnet_prefix); EXPECT_EQ("", props.peer_address); config["ifconfig_loCal"] = "4.5.6.7"; config["ifconfiG_broadcast"] = "1.2.255.255"; config["ifconFig_netmAsk"] = "255.255.255.0"; config["ifconfig_remotE"] = "33.44.55.66"; config["route_vpN_gateway"] = "192.168.1.1"; config["trusted_ip"] = "99.88.77.66"; config["tun_mtu"] = "1000"; config["foreign_option_2"] = "dhcp-option DNS 4.4.4.4"; config["foreign_option_1"] = "dhcp-option DNS 1.1.1.1"; config["foreign_option_3"] = "dhcp-option DNS 2.2.2.2"; config["route_network_2"] = kNetwork2; config["route_network_1"] = kNetwork1; config["route_netmask_2"] = kNetmask2; config["route_netmask_1"] = kNetmask1; config["route_gateway_2"] = kGateway2; config["route_gateway_1"] = kGateway1; config["foo"] = "bar"; driver_->ParseIPConfiguration(config, &props); EXPECT_EQ(IPAddress::kFamilyIPv4, props.address_family); EXPECT_EQ("4.5.6.7", props.address); EXPECT_EQ("1.2.255.255", props.broadcast_address); EXPECT_EQ(24, props.subnet_prefix); EXPECT_EQ("33.44.55.66", props.peer_address); EXPECT_EQ("192.168.1.1", props.gateway); EXPECT_EQ("99.88.77.66/32", props.exclusion_list[0]); EXPECT_EQ(1, props.exclusion_list.size()); EXPECT_EQ(1000, props.mtu); ASSERT_EQ(3, props.dns_servers.size()); EXPECT_EQ("1.1.1.1", props.dns_servers[0]); EXPECT_EQ("4.4.4.4", props.dns_servers[1]); EXPECT_EQ("2.2.2.2", props.dns_servers[2]); ASSERT_EQ(2, props.routes.size()); EXPECT_EQ(kGateway1, props.routes[0].gateway); EXPECT_EQ(kNetmask1, props.routes[0].netmask); EXPECT_EQ(kNetwork1, props.routes[0].host); EXPECT_EQ(kGateway2, props.routes[1].gateway); EXPECT_EQ(kNetmask2, props.routes[1].netmask); EXPECT_EQ(kNetwork2, props.routes[1].host); EXPECT_FALSE(props.blackhole_ipv6); // If the driver is configured to ignore the gateway provided, it will // not set the "gateway" property for the properties, however the // explicitly supplied routes should still be set. SetArg(kOpenVPNIgnoreDefaultRouteProperty, "some value"); IPConfig::Properties props_without_gateway; driver_->ParseIPConfiguration(config, &props_without_gateway); EXPECT_EQ(kGateway1, props_without_gateway.routes[0].gateway); EXPECT_EQ("", props_without_gateway.gateway); // A pushed redirect flag should override the IgnoreDefaultRoute property. config["redirect_gateway"] = "def1"; IPConfig::Properties props_with_override; driver_->ParseIPConfiguration(config, &props_with_override); EXPECT_EQ("192.168.1.1", props_with_override.gateway); } TEST_F(OpenVPNDriverTest, InitOptionsNoHost) { Error error; vector<vector<string>> options; driver_->InitOptions(&options, &error); EXPECT_EQ(Error::kInvalidArguments, error.type()); EXPECT_TRUE(options.empty()); } TEST_F(OpenVPNDriverTest, InitOptions) { static const char kHost[] = "192.168.2.254"; static const char kTLSAuthContents[] = "SOME-RANDOM-CONTENTS\n"; static const char kID[] = "TestPKCS11ID"; static const char kKU0[] = "00"; static const char kKU1[] = "01"; FilePath empty_cert; SetArg(kProviderHostProperty, kHost); SetArg(kOpenVPNTLSAuthContentsProperty, kTLSAuthContents); SetArg(kOpenVPNClientCertIdProperty, kID); SetArg(kOpenVPNRemoteCertKUProperty, string(kKU0) + " " + string(kKU1)); driver_->rpc_task_.reset(new RPCTask(&control_, this)); driver_->tunnel_interface_ = kInterfaceName; EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false)); Error error; vector<vector<string>> options; driver_->InitOptions(&options, &error); EXPECT_TRUE(error.IsSuccess()); EXPECT_EQ(vector<string> { "client" }, options[0]); ExpectInFlags(options, "remote", kHost); ExpectInFlags(options, vector<string> { "setenv", kRPCTaskPathVariable, RPCTaskMockAdaptor::kRpcId }); ExpectInFlags(options, "dev", kInterfaceName); ExpectInFlags(options, "group", "openvpn"); EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_); ASSERT_FALSE(driver_->tls_auth_file_.empty()); ExpectInFlags(options, "tls-auth", driver_->tls_auth_file_.value()); string contents; EXPECT_TRUE(base::ReadFileToString(driver_->tls_auth_file_, &contents)); EXPECT_EQ(kTLSAuthContents, contents); ExpectInFlags(options, "pkcs11-id", kID); ExpectInFlags(options, "ca", OpenVPNDriver::kDefaultCACertificates); ExpectInFlags(options, "syslog"); ExpectNotInFlags(options, "auth-user-pass"); ExpectInFlags(options, vector<string> { "remote-cert-ku", kKU0, kKU1 }); } TEST_F(OpenVPNDriverTest, InitOptionsHostWithPort) { SetArg(kProviderHostProperty, "v.com:1234"); driver_->rpc_task_.reset(new RPCTask(&control_, this)); driver_->tunnel_interface_ = kInterfaceName; EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false)); Error error; vector<vector<string>> options; driver_->InitOptions(&options, &error); EXPECT_TRUE(error.IsSuccess()); ExpectInFlags(options, vector<string> { "remote", "v.com", "1234" }); } TEST_F(OpenVPNDriverTest, InitCAOptions) { static const char kHost[] = "192.168.2.254"; static const char kCaCert[] = "foo"; static const char kCaCertNSS[] = "{1234}"; Error error; vector<vector<string>> options; EXPECT_TRUE(driver_->InitCAOptions(&options, &error)); EXPECT_TRUE(error.IsSuccess()); ExpectInFlags(options, "ca", OpenVPNDriver::kDefaultCACertificates); options.clear(); SetArg(kOpenVPNCaCertProperty, kCaCert); EXPECT_TRUE(driver_->InitCAOptions(&options, &error)); ExpectInFlags(options, "ca", kCaCert); EXPECT_TRUE(error.IsSuccess()); // We should ignore the CaCertNSS property. SetArg(kOpenVPNCaCertNSSProperty, kCaCertNSS); EXPECT_TRUE(driver_->InitCAOptions(&options, &error)); ExpectInFlags(options, "ca", kCaCert); EXPECT_TRUE(error.IsSuccess()); SetArg(kOpenVPNCaCertProperty, ""); SetArg(kProviderHostProperty, kHost); FilePath empty_cert; error.Reset(); EXPECT_TRUE(driver_->InitCAOptions(&options, &error)); ExpectInFlags(options, "ca", OpenVPNDriver::kDefaultCACertificates); EXPECT_TRUE(error.IsSuccess()); SetArg(kOpenVPNCaCertProperty, kCaCert); const vector<string> kCaCertPEM{ "---PEM CONTENTS---" }; SetArgArray(kOpenVPNCaCertPemProperty, kCaCertPEM); EXPECT_FALSE(driver_->InitCAOptions(&options, &error)); EXPECT_EQ(Error::kInvalidArguments, error.type()); EXPECT_EQ("Can't specify more than one of CACert and CACertPEM.", error.message()); options.clear(); SetArg(kOpenVPNCaCertProperty, ""); SetArg(kProviderHostProperty, ""); static const char kPEMCertfile[] = "/tmp/pem-cert"; FilePath pem_cert(kPEMCertfile); EXPECT_CALL(*certificate_file_, CreatePEMFromStrings(kCaCertPEM)) .WillOnce(Return(empty_cert)) .WillOnce(Return(pem_cert)); error.Reset(); EXPECT_FALSE(driver_->InitCAOptions(&options, &error)); EXPECT_EQ(Error::kInvalidArguments, error.type()); EXPECT_EQ("Unable to extract PEM CA certificates.", error.message()); error.Reset(); options.clear(); EXPECT_TRUE(driver_->InitCAOptions(&options, &error)); ExpectInFlags(options, "ca", kPEMCertfile); EXPECT_TRUE(error.IsSuccess()); } TEST_F(OpenVPNDriverTest, InitCertificateVerifyOptions) { { Error error; vector<vector<string>> options; // No options supplied. driver_->InitCertificateVerifyOptions(&options); EXPECT_TRUE(options.empty()); } const char kName[] = "x509-name"; { Error error; vector<vector<string>> options; // With Name property alone, we should have the 1-parameter version of the // "x509-verify-name" parameter provided. SetArg(kOpenVPNVerifyX509NameProperty, kName); driver_->InitCertificateVerifyOptions(&options); ExpectInFlags(options, "verify-x509-name", kName); } const char kType[] = "x509-type"; { Error error; vector<vector<string>> options; // With both Name property and Type property set, we should have the // 2-parameter version of the "x509-verify-name" parameter provided. SetArg(kOpenVPNVerifyX509TypeProperty, kType); driver_->InitCertificateVerifyOptions(&options); ExpectInFlags(options, vector<string> { "verify-x509-name", kName, kType }); } { Error error; vector<vector<string>> options; // We should ignore the Type parameter if no Name parameter is specified. SetArg(kOpenVPNVerifyX509NameProperty, ""); driver_->InitCertificateVerifyOptions(&options); EXPECT_TRUE(options.empty()); } } TEST_F(OpenVPNDriverTest, InitClientAuthOptions) { static const char kTestValue[] = "foo"; vector<vector<string>> options; // No key or cert, assume user/password authentication. driver_->InitClientAuthOptions(&options); ExpectInFlags(options, "auth-user-pass"); ExpectNotInFlags(options, "key"); ExpectNotInFlags(options, "cert"); // Cert available, no user/password. options.clear(); SetArg(kOpenVPNCertProperty, kTestValue); driver_->InitClientAuthOptions(&options); ExpectNotInFlags(options, "auth-user-pass"); ExpectNotInFlags(options, "key"); ExpectInFlags(options, "cert", kTestValue); // Key available, no user/password. options.clear(); SetArg(kOpenVPNKeyProperty, kTestValue); driver_->InitClientAuthOptions(&options); ExpectNotInFlags(options, "auth-user-pass"); ExpectInFlags(options, "key", kTestValue); // Key available, AuthUserPass set. options.clear(); SetArg(kOpenVPNAuthUserPassProperty, kTestValue); driver_->InitClientAuthOptions(&options); ExpectInFlags(options, "auth-user-pass"); ExpectInFlags(options, "key", kTestValue); // Key available, User set. options.clear(); RemoveStringArg(kOpenVPNAuthUserPassProperty); SetArg(kOpenVPNUserProperty, "user"); driver_->InitClientAuthOptions(&options); ExpectInFlags(options, "auth-user-pass"); ExpectInFlags(options, "key", kTestValue); // Empty PKCS11 certificate id, no user/password/cert. options.clear(); RemoveStringArg(kOpenVPNKeyProperty); RemoveStringArg(kOpenVPNCertProperty); RemoveStringArg(kOpenVPNUserProperty); SetArg(kOpenVPNClientCertIdProperty, ""); driver_->InitClientAuthOptions(&options); ExpectInFlags(options, "auth-user-pass"); ExpectNotInFlags(options, "key"); ExpectNotInFlags(options, "cert"); ExpectNotInFlags(options, "pkcs11-id"); // Non-empty PKCS11 certificate id, no user/password/cert. options.clear(); SetArg(kOpenVPNClientCertIdProperty, kTestValue); driver_->InitClientAuthOptions(&options); ExpectNotInFlags(options, "auth-user-pass"); ExpectNotInFlags(options, "key"); ExpectNotInFlags(options, "cert"); // The "--pkcs11-id" option is added in InitPKCS11Options(), not here. ExpectNotInFlags(options, "pkcs11-id"); // PKCS11 certificate id available, AuthUserPass set. options.clear(); SetArg(kOpenVPNAuthUserPassProperty, kTestValue); driver_->InitClientAuthOptions(&options); ExpectInFlags(options, "auth-user-pass"); ExpectNotInFlags(options, "key"); ExpectNotInFlags(options, "cert"); // PKCS11 certificate id available, User set. options.clear(); RemoveStringArg(kOpenVPNAuthUserPassProperty); SetArg(kOpenVPNUserProperty, "user"); driver_->InitClientAuthOptions(&options); ExpectInFlags(options, "auth-user-pass"); ExpectNotInFlags(options, "key"); ExpectNotInFlags(options, "cert"); } TEST_F(OpenVPNDriverTest, InitExtraCertOptions) { { Error error; vector<vector<string>> options; // No ExtraCertOptions supplied. EXPECT_TRUE(driver_->InitExtraCertOptions(&options, &error)); EXPECT_TRUE(error.IsSuccess()); EXPECT_TRUE(options.empty()); } { Error error; vector<vector<string>> options; SetArgArray(kOpenVPNExtraCertPemProperty, vector<string>()); // Empty ExtraCertOptions supplied. EXPECT_TRUE(driver_->InitExtraCertOptions(&options, &error)); EXPECT_TRUE(error.IsSuccess()); EXPECT_TRUE(options.empty()); } const vector<string> kExtraCerts{ "---PEM CONTENTS---" }; SetArgArray(kOpenVPNExtraCertPemProperty, kExtraCerts); static const char kPEMCertfile[] = "/tmp/pem-cert"; FilePath pem_cert(kPEMCertfile); EXPECT_CALL(*extra_certificates_file_, CreatePEMFromStrings(kExtraCerts)) .WillOnce(Return(FilePath())) .WillOnce(Return(pem_cert)); // CreatePemFromStrings fails. { Error error; vector<vector<string>> options; EXPECT_FALSE(driver_->InitExtraCertOptions(&options, &error)); EXPECT_EQ(Error::kInvalidArguments, error.type()); EXPECT_TRUE(options.empty()); } // CreatePemFromStrings succeeds. { Error error; vector<vector<string>> options; EXPECT_TRUE(driver_->InitExtraCertOptions(&options, &error)); EXPECT_TRUE(error.IsSuccess()); ExpectInFlags(options, "extra-certs", kPEMCertfile); } } TEST_F(OpenVPNDriverTest, InitPKCS11Options) { vector<vector<string>> options; driver_->InitPKCS11Options(&options); EXPECT_TRUE(options.empty()); static const char kID[] = "TestPKCS11ID"; SetArg(kOpenVPNClientCertIdProperty, kID); driver_->InitPKCS11Options(&options); ExpectInFlags(options, "pkcs11-id", kID); ExpectInFlags(options, "pkcs11-providers", "libchaps.so"); static const char kProvider[] = "libpkcs11.so"; SetArg(kOpenVPNProviderProperty, kProvider); options.clear(); driver_->InitPKCS11Options(&options); ExpectInFlags(options, "pkcs11-id", kID); ExpectInFlags(options, "pkcs11-providers", kProvider); } TEST_F(OpenVPNDriverTest, InitManagementChannelOptionsServerFail) { vector<vector<string>> options; EXPECT_CALL(*management_server_, Start(&dispatcher_, GetSockets(), &options)) .WillOnce(Return(false)); Error error; EXPECT_FALSE(InitManagementChannelOptions(&options, &error)); EXPECT_EQ(Error::kInternalError, error.type()); EXPECT_EQ("Unable to setup management channel.", error.message()); } TEST_F(OpenVPNDriverTest, InitManagementChannelOptionsOnline) { vector<vector<string>> options; EXPECT_CALL(*management_server_, Start(&dispatcher_, GetSockets(), &options)) .WillOnce(Return(true)); EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(true)); EXPECT_CALL(*management_server_, ReleaseHold()); Error error; EXPECT_TRUE(InitManagementChannelOptions(&options, &error)); EXPECT_TRUE(error.IsSuccess()); } TEST_F(OpenVPNDriverTest, InitManagementChannelOptionsOffline) { vector<vector<string>> options; EXPECT_CALL(*management_server_, Start(&dispatcher_, GetSockets(), &options)) .WillOnce(Return(true)); EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false)); EXPECT_CALL(*management_server_, ReleaseHold()).Times(0); Error error; EXPECT_TRUE(InitManagementChannelOptions(&options, &error)); EXPECT_TRUE(error.IsSuccess()); } TEST_F(OpenVPNDriverTest, InitLoggingOptions) { vector<vector<string>> options; bool vpn_logging = SLOG_IS_ON(VPN, 0); ScopeLogger::GetInstance()->EnableScopesByName("-vpn"); driver_->InitLoggingOptions(&options); ASSERT_EQ(1, options.size()); EXPECT_EQ(vector<string> { "syslog" }, options[0]); ScopeLogger::GetInstance()->EnableScopesByName("+vpn"); options.clear(); driver_->InitLoggingOptions(&options); ExpectInFlags(options, "verb", "3"); ScopeLogger::GetInstance()->EnableScopesByName("-vpn"); SetArg("OpenVPN.Verb", "2"); options.clear(); driver_->InitLoggingOptions(&options); ExpectInFlags(options, "verb", "2"); ScopeLogger::GetInstance()->EnableScopesByName("+vpn"); SetArg("OpenVPN.Verb", "1"); options.clear(); driver_->InitLoggingOptions(&options); ExpectInFlags(options, "verb", "1"); if (!vpn_logging) { ScopeLogger::GetInstance()->EnableScopesByName("-vpn"); } } TEST_F(OpenVPNDriverTest, AppendValueOption) { vector<vector<string>> options; EXPECT_FALSE( driver_->AppendValueOption("OpenVPN.UnknownProperty", kOption, &options)); EXPECT_TRUE(options.empty()); SetArg(kProperty, ""); EXPECT_FALSE(driver_->AppendValueOption(kProperty, kOption, &options)); EXPECT_TRUE(options.empty()); SetArg(kProperty, kValue); SetArg(kProperty2, kValue2); EXPECT_TRUE(driver_->AppendValueOption(kProperty, kOption, &options)); EXPECT_TRUE(driver_->AppendValueOption(kProperty2, kOption2, &options)); EXPECT_EQ(2, options.size()); vector<string> expected_value { kOption, kValue }; EXPECT_EQ(expected_value, options[0]); vector<string> expected_value2 { kOption2, kValue2 }; EXPECT_EQ(expected_value2, options[1]); } TEST_F(OpenVPNDriverTest, AppendDelimitedValueOption) { vector<vector<string>> options; EXPECT_FALSE( driver_->AppendDelimitedValueOption( "OpenVPN.UnknownProperty", kOption, ' ', &options)); EXPECT_TRUE(options.empty()); SetArg(kProperty, ""); EXPECT_FALSE( driver_->AppendDelimitedValueOption(kProperty, kOption, ' ', &options)); EXPECT_TRUE(options.empty()); string kConcatenatedValues(string(kValue) + " " + string(kValue2)); SetArg(kProperty, kConcatenatedValues); SetArg(kProperty2, kConcatenatedValues); EXPECT_TRUE(driver_->AppendDelimitedValueOption( kProperty, kOption, ':', &options)); EXPECT_TRUE(driver_->AppendDelimitedValueOption( kProperty2, kOption2, ' ', &options)); EXPECT_EQ(2, options.size()); vector<string> expected_value { kOption, kConcatenatedValues }; EXPECT_EQ(expected_value, options[0]); vector<string> expected_value2 { kOption2, kValue, kValue2 }; EXPECT_EQ(expected_value2, options[1]); } TEST_F(OpenVPNDriverTest, AppendFlag) { vector<vector<string>> options; EXPECT_FALSE( driver_->AppendFlag("OpenVPN.UnknownProperty", kOption, &options)); EXPECT_TRUE(options.empty()); SetArg(kProperty, ""); SetArg(kProperty2, kValue2); EXPECT_TRUE(driver_->AppendFlag(kProperty, kOption, &options)); EXPECT_TRUE(driver_->AppendFlag(kProperty2, kOption2, &options)); EXPECT_EQ(2, options.size()); EXPECT_EQ(vector<string> { kOption }, options[0]); EXPECT_EQ(vector<string> { kOption2 }, options[1]); } TEST_F(OpenVPNDriverTest, ClaimInterface) { driver_->tunnel_interface_ = kInterfaceName; EXPECT_FALSE(driver_->ClaimInterface(string(kInterfaceName) + "XXX", kInterfaceIndex)); EXPECT_FALSE(driver_->device_); static const char kHost[] = "192.168.2.254"; SetArg(kProviderHostProperty, kHost); EXPECT_CALL(*management_server_, Start(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false)); EXPECT_CALL( process_manager_, StartProcess(_, _, _, _, false /* Don't exit with parent */, _)) .WillOnce(Return(true)); const int kServiceCallbackTag = 1; EXPECT_EQ(0, driver_->default_service_callback_tag_); EXPECT_CALL(manager_, RegisterDefaultServiceCallback(_)) .WillOnce(Return(kServiceCallbackTag)); EXPECT_TRUE(driver_->ClaimInterface(kInterfaceName, kInterfaceIndex)); ASSERT_TRUE(driver_->device_); EXPECT_EQ(kInterfaceIndex, driver_->device_->interface_index()); EXPECT_EQ(kServiceCallbackTag, driver_->default_service_callback_tag_); } TEST_F(OpenVPNDriverTest, IdleService) { SetService(service_); EXPECT_CALL(*service_, SetState(Service::kStateIdle)); driver_->IdleService(); } TEST_F(OpenVPNDriverTest, FailService) { static const char kErrorDetails[] = "Bad password."; SetService(service_); EXPECT_CALL(*service_, SetFailure(Service::kFailureConnect)); driver_->FailService(Service::kFailureConnect, kErrorDetails); EXPECT_EQ(kErrorDetails, service_->error_details()); } TEST_F(OpenVPNDriverTest, Cleanup) { // Ensure no crash. driver_->Cleanup(Service::kStateIdle, Service::kFailureUnknown, Service::kErrorDetailsNone); const int kPID = 123456; const int kServiceCallbackTag = 5; static const char kErrorDetails[] = "Certificate revoked."; driver_->default_service_callback_tag_ = kServiceCallbackTag; driver_->pid_ = kPID; driver_->rpc_task_.reset(new RPCTask(&control_, this)); driver_->tunnel_interface_ = kInterfaceName; driver_->device_ = device_; driver_->service_ = service_; driver_->ip_properties_.address = "1.2.3.4"; StartConnectTimeout(0); FilePath tls_auth_file; EXPECT_TRUE(base::CreateTemporaryFile(&tls_auth_file)); EXPECT_FALSE(tls_auth_file.empty()); EXPECT_TRUE(base::PathExists(tls_auth_file)); driver_->tls_auth_file_ = tls_auth_file; // Stop will be called twice -- once by Cleanup and once by the destructor. EXPECT_CALL(*management_server_, Stop()).Times(2); // UpdateExitCallback will be called twice -- once to ignore exit, // and once to re-enabling monitoring of exit. EXPECT_CALL(process_manager_, UpdateExitCallback(kPID, _)).Times(2); EXPECT_CALL(manager_, DeregisterDefaultServiceCallback(kServiceCallbackTag)); EXPECT_CALL(process_manager_, StopProcess(kPID)); EXPECT_CALL(device_info_, DeleteInterface(_)).Times(0); EXPECT_CALL(*device_, DropConnection()); EXPECT_CALL(*device_, SetEnabled(false)); EXPECT_CALL(*service_, SetFailure(Service::kFailureInternal)); driver_->Cleanup( Service::kStateFailure, Service::kFailureInternal, kErrorDetails); EXPECT_EQ(0, driver_->default_service_callback_tag_); EXPECT_EQ(0, driver_->pid_); EXPECT_FALSE(driver_->rpc_task_.get()); EXPECT_TRUE(driver_->tunnel_interface_.empty()); EXPECT_FALSE(driver_->device_); EXPECT_FALSE(driver_->service_); EXPECT_EQ(kErrorDetails, service_->error_details()); EXPECT_FALSE(base::PathExists(tls_auth_file)); EXPECT_TRUE(driver_->tls_auth_file_.empty()); EXPECT_TRUE(driver_->ip_properties_.address.empty()); EXPECT_FALSE(driver_->IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, SpawnOpenVPN) { SetupLSBRelease(); EXPECT_FALSE(driver_->SpawnOpenVPN()); static const char kHost[] = "192.168.2.254"; SetArg(kProviderHostProperty, kHost); driver_->tunnel_interface_ = "tun0"; driver_->rpc_task_.reset(new RPCTask(&control_, this)); EXPECT_CALL(*management_server_, Start(_, _, _)) .Times(2) .WillRepeatedly(Return(true)); EXPECT_CALL(manager_, IsConnected()).Times(2).WillRepeatedly(Return(false)); const int kPID = 234678; const map<string, string> expected_env{ {"IV_PLAT", "Chromium OS"}, {"IV_PLAT_REL", "2202.0"}}; EXPECT_CALL(process_manager_, StartProcess(_, _, _, expected_env, _, _)) .WillOnce(Return(-1)) .WillOnce(Return(kPID)); EXPECT_FALSE(driver_->SpawnOpenVPN()); EXPECT_TRUE(driver_->SpawnOpenVPN()); EXPECT_EQ(kPID, driver_->pid_); } TEST_F(OpenVPNDriverTest, OnOpenVPNDied) { const int kPID = 99999; driver_->device_ = device_; driver_->pid_ = kPID; EXPECT_CALL(*device_, DropConnection()); EXPECT_CALL(*device_, SetEnabled(false)); EXPECT_CALL(process_manager_, StopProcess(_)).Times(0); EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex)); driver_->OnOpenVPNDied(2); EXPECT_EQ(0, driver_->pid_); } TEST_F(OpenVPNDriverTest, Disconnect) { driver_->device_ = device_; driver_->service_ = service_; EXPECT_CALL(*device_, DropConnection()); EXPECT_CALL(*device_, SetEnabled(false)); EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex)); EXPECT_CALL(*service_, SetState(Service::kStateIdle)); driver_->Disconnect(); EXPECT_FALSE(driver_->device_); EXPECT_FALSE(driver_->service_); } TEST_F(OpenVPNDriverTest, OnConnectionDisconnected) { EXPECT_CALL(*management_server_, Restart()); SetDevice(device_); SetService(service_); EXPECT_CALL(*device_, DropConnection()); EXPECT_CALL(*service_, SetState(Service::kStateAssociating)); OnConnectionDisconnected(); EXPECT_TRUE(IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, OnConnectTimeout) { StartConnectTimeout(0); SetService(service_); EXPECT_CALL(*service_, SetFailure(Service::kFailureConnect)); OnConnectTimeout(); EXPECT_FALSE(GetService()); EXPECT_FALSE(IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, OnConnectTimeoutResolve) { StartConnectTimeout(0); SetService(service_); SetClientState(OpenVPNManagementServer::kStateResolve); EXPECT_CALL(*service_, SetFailure(Service::kFailureDNSLookup)); OnConnectTimeout(); EXPECT_FALSE(GetService()); EXPECT_FALSE(IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, OnReconnectingUnknown) { EXPECT_FALSE(IsConnectTimeoutStarted()); EXPECT_CALL(dispatcher_, PostDelayedTask(_, GetDefaultConnectTimeoutSeconds() * 1000)); SetDevice(device_); SetService(service_); EXPECT_CALL(*device_, DropConnection()); EXPECT_CALL(*service_, SetState(Service::kStateAssociating)); driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonUnknown); EXPECT_TRUE(IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, OnReconnectingTLSError) { EXPECT_CALL(dispatcher_, PostDelayedTask(_, GetReconnectOfflineTimeoutSeconds() * 1000)); EXPECT_CALL(dispatcher_, PostDelayedTask(_, GetReconnectTLSErrorTimeoutSeconds() * 1000)); driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonOffline); EXPECT_TRUE(IsConnectTimeoutStarted()); // The scheduled timeout should not be affected for unknown reason. driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonUnknown); EXPECT_TRUE(IsConnectTimeoutStarted()); // Reconnect on TLS error reschedules the timeout once. driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonTLSError); EXPECT_TRUE(IsConnectTimeoutStarted()); driver_->OnReconnecting(OpenVPNDriver::kReconnectReasonTLSError); EXPECT_TRUE(IsConnectTimeoutStarted()); } TEST_F(OpenVPNDriverTest, InitPropertyStore) { // Sanity test property store initialization. PropertyStore store; driver_->InitPropertyStore(&store); const string kUser = "joe"; Error error; EXPECT_TRUE(store.SetStringProperty(kOpenVPNUserProperty, kUser, &error)); EXPECT_TRUE(error.IsSuccess()); EXPECT_EQ(kUser, GetArgs()->LookupString(kOpenVPNUserProperty, "")); } TEST_F(OpenVPNDriverTest, PassphraseRequired) { PropertyStore store; driver_->InitPropertyStore(&store); KeyValueStore props = GetProviderProperties(store); EXPECT_TRUE(props.LookupBool(kPassphraseRequiredProperty, false)); SetArg(kOpenVPNPasswordProperty, "random-password"); props = GetProviderProperties(store); EXPECT_FALSE(props.LookupBool(kPassphraseRequiredProperty, true)); // This parameter should be write-only. EXPECT_FALSE(props.ContainsString(kOpenVPNPasswordProperty)); SetArg(kOpenVPNPasswordProperty, ""); props = GetProviderProperties(store); EXPECT_TRUE(props.LookupBool(kPassphraseRequiredProperty, false)); SetArg(kOpenVPNTokenProperty, "random-token"); props = GetProviderProperties(store); EXPECT_FALSE(props.LookupBool(kPassphraseRequiredProperty, true)); // This parameter should be write-only. EXPECT_FALSE(props.ContainsString(kOpenVPNTokenProperty)); } TEST_F(OpenVPNDriverTest, GetEnvironment) { SetupLSBRelease(); const map<string, string> expected{ {"IV_PLAT", "Chromium OS"}, {"IV_PLAT_REL", "2202.0"}}; ASSERT_EQ(expected, driver_->GetEnvironment()); EXPECT_EQ(0, base::WriteFile(lsb_release_file_, "", 0)); EXPECT_EQ(0, driver_->GetEnvironment().size()); } TEST_F(OpenVPNDriverTest, OnOpenVPNExited) { const int kExitStatus = 1; std::unique_ptr<MockDeviceInfo> device_info( new MockDeviceInfo(&control_, &dispatcher_, &metrics_, &manager_)); EXPECT_CALL(*device_info, DeleteInterface(kInterfaceIndex)) .WillOnce(Return(true)); WeakPtr<DeviceInfo> weak = device_info->AsWeakPtr(); EXPECT_TRUE(weak); OpenVPNDriver::OnOpenVPNExited(weak, kInterfaceIndex, kExitStatus); device_info.reset(); EXPECT_FALSE(weak); // Expect no crash. OpenVPNDriver::OnOpenVPNExited(weak, kInterfaceIndex, kExitStatus); } TEST_F(OpenVPNDriverTest, OnDefaultServiceChanged) { driver_->service_ = service_; ServiceRefPtr null_service; EXPECT_CALL(*management_server_, Hold()); driver_->OnDefaultServiceChanged(null_service); EXPECT_CALL(*management_server_, Hold()); driver_->OnDefaultServiceChanged(service_); scoped_refptr<MockService> mock_service( new MockService(&control_, &dispatcher_, &metrics_, &manager_)); EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(false)); EXPECT_CALL(*management_server_, Hold()); driver_->OnDefaultServiceChanged(mock_service); EXPECT_CALL(*mock_service, IsConnected()).WillOnce(Return(true)); EXPECT_CALL(*management_server_, ReleaseHold()); driver_->OnDefaultServiceChanged(mock_service); } TEST_F(OpenVPNDriverTest, GetReconnectTimeoutSeconds) { EXPECT_EQ(GetDefaultConnectTimeoutSeconds(), GetReconnectTimeoutSeconds(OpenVPNDriver::kReconnectReasonUnknown)); EXPECT_EQ(GetReconnectOfflineTimeoutSeconds(), GetReconnectTimeoutSeconds(OpenVPNDriver::kReconnectReasonOffline)); EXPECT_EQ(GetReconnectTLSErrorTimeoutSeconds(), GetReconnectTimeoutSeconds( OpenVPNDriver::kReconnectReasonTLSError)); } TEST_F(OpenVPNDriverTest, WriteConfigFile) { const char kOption0[] = "option0"; const char kOption1[] = "option1"; const char kOption1Argument0[] = "option1-argument0"; const char kOption2[] = "option2"; const char kOption2Argument0[] = "option2-argument0\n\t\"'\\"; const char kOption2Argument0Transformed[] = "option2-argument0 \t\\\"'\\\\"; const char kOption2Argument1[] = "option2-argument1 space"; vector<vector<string>> options { { kOption0 }, { kOption1, kOption1Argument0 }, { kOption2, kOption2Argument0, kOption2Argument1 } }; FilePath config_directory( temporary_directory_.path().Append(kOpenVPNConfigDirectory)); FilePath config_file; EXPECT_FALSE(base::PathExists(config_directory)); EXPECT_TRUE(driver_->WriteConfigFile(options, &config_file)); EXPECT_TRUE(base::PathExists(config_directory)); EXPECT_TRUE(base::PathExists(config_file)); EXPECT_TRUE(config_directory.IsParent(config_file)); string config_contents; EXPECT_TRUE(base::ReadFileToString(config_file, &config_contents)); string expected_config_contents = base::StringPrintf( "%s\n%s %s\n%s \"%s\" \"%s\"\n", kOption0, kOption1, kOption1Argument0, kOption2, kOption2Argument0Transformed, kOption2Argument1); EXPECT_EQ(expected_config_contents, config_contents); } } // namespace shill