blob: 88e2762c614c91ee6f5ed52d434c9af9bda2d452 [file] [log] [blame]
# Copyright (c) 2013 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.
import logging
import time
from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros import site_eap_certs
from autotest_lib.client.cros import hostapd_server
from autotest_lib.client.cros import shill_temporary_profile
from autotest_lib.client.cros import virtual_ethernet_pair
# This hacks the path so that we can import shill_proxy.
# pylint: disable=W0611
from autotest_lib.client.cros import flimflam_test_path
# pylint: enable=W0611
import shill_proxy
class network_8021xWiredAuthentication(test.test):
"""The 802.1x EAP wired authentication class.
Runs hostapd on one side of an ethernet pair, and shill on the other.
Configures the Ethernet service with 802.1x credentials and ensures
that when shill detects an EAP authenticator, it is successful in
using its credentials to gain access.
"""
INTERFACE_NAME = 'pseudoethernet0'
AUTHENTICATION_FLAG = 'EapAuthenticationCompleted'
TEST_PROFILE_NAME = 'test1x'
AUTHENTICATION_TIMEOUT = 10
version = 1
def get_device(self, interface_name):
"""Finds the corresponding Device object for an ethernet
interface with the name |interface_name|.
@param interface_name string The name of the interface to check.
@return DBus interface object representing the associated device.
"""
device = self._shill_proxy.find_object('Device',
{'Name': interface_name})
if device is None:
raise error.TestFail('Device was not found.')
return device
def get_authenticated_flag(self, interface_name):
"""Checks whether |interface_name| has successfully negotiated
802.1x.
@param interface_name string The name of the interface to check.
@return True if the authenticated flag is set, False otherwise.
"""
device = self.get_device(interface_name)
device_properties = device.GetProperties(utf8_strings=True)
logging.info('Device properties are %r', device_properties)
return shill_proxy.ShillProxy.dbus2primitive(
device_properties[self.AUTHENTICATION_FLAG])
def wait_for_authentication(self, interface_name):
"""Wait for |interface_name| to get to enter authentication state.
@param interface_name string The name of the interface to check.
"""
device = self.get_device(interface_name)
result = self._shill_proxy.wait_for_property_in(
device,
self.AUTHENTICATION_FLAG,
(True,),
self.AUTHENTICATION_TIMEOUT)
(successful, _, _) = result
return successful
def configure_credentials(self, interface_name):
"""Adds authentication properties to the Ethernet EAP service.
@param interface_name string The name of the associated interface
"""
service = self._shill_proxy.manager.ConfigureService({
'Type': 'etherneteap',
'EAP.EAP': hostapd_server.HostapdServer.EAP_TYPE,
'EAP.InnerEAP': 'auth=%s' % hostapd_server.HostapdServer.EAP_PHASE2,
'EAP.Identity': hostapd_server.HostapdServer.EAP_USERNAME,
'EAP.Password': hostapd_server.HostapdServer.EAP_PASSWORD,
'EAP.CACertPEM': site_eap_certs.ca_cert_1
})
def run_once(self):
"""Test main loop."""
self._shill_proxy = shill_proxy.ShillProxy()
manager = self._shill_proxy.manager
with shill_temporary_profile.ShillTemporaryProfile(
manager, profile_name=self.TEST_PROFILE_NAME):
with virtual_ethernet_pair.VirtualEthernetPair(
peer_interface_name=self.INTERFACE_NAME,
peer_interface_ip=None) as ethernet_pair:
if not ethernet_pair.is_healthy:
raise error.TestFail('Virtual ethernet pair failed.')
if self.get_authenticated_flag(self.INTERFACE_NAME):
raise error.TestFail('Authentication flag already set.')
with hostapd_server.HostapdServer(
interface=ethernet_pair.interface_name) as hostapd:
# Wait for hostapd to initialize.
time.sleep(1)
if not hostapd.running():
raise error.TestFail('hostapd process exited.')
self.configure_credentials(self.INTERFACE_NAME)
hostapd.send_eap_packets()
if not self.wait_for_authentication(self.INTERFACE_NAME):
raise error.TestFail('Authentication did not complete.')
client_mac_address = ethernet_pair.peer_interface_mac
if not hostapd.client_has_authenticated(client_mac_address):
raise error.TestFail('Server does not agree that '
'client is authenticated')
if hostapd.client_has_logged_off(client_mac_address):
raise error.TestFail('Client has already logged off')
# Since the EAP credentials are associated with the
# top-most profile, popping it should cause the client
# to immediately log-off.
manager.PopProfile(self.TEST_PROFILE_NAME)
if self.get_authenticated_flag(self.INTERFACE_NAME):
raise error.TestFail('Client is still authenticated.')
if not hostapd.client_has_logged_off(client_mac_address):
raise error.TestFail('Client did not log off')
# Re-pushing the profile should make the EAP credentials
# available again, and should cause the client to
# re-authenticate.
manager.PushProfile(self.TEST_PROFILE_NAME)
if not self.wait_for_authentication(self.INTERFACE_NAME):
raise error.TestFail('Re-authentication did not '
'complete.')