1# Lint as: python2, python3 2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import absolute_import 7from __future__ import division 8from __future__ import print_function 9 10import logging 11import os 12import random 13import six 14from six.moves import map 15from six.moves import range 16import stat 17import string 18import sys 19import tempfile 20 21from autotest_lib.client.common_lib import error 22from autotest_lib.client.common_lib.cros import xmlrpc_types 23 24 25def deserialize(serialized): 26 """Deserialize a SecurityConfig. 27 28 @param serialized dict representing a serialized SecurityConfig. 29 @return a SecurityConfig object built from |serialized|. 30 31 """ 32 return xmlrpc_types.deserialize(serialized, module=sys.modules[__name__]) 33 34 35class SecurityConfig(xmlrpc_types.XmlRpcStruct): 36 """Abstracts the security configuration for a WiFi network. 37 38 This bundle of credentials can be passed to both HostapConfig and 39 AssociationParameters so that both shill and hostapd can set up and connect 40 to an encrypted WiFi network. By default, we'll assume we're connecting 41 to an open network. 42 43 """ 44 SERVICE_PROPERTY_PASSPHRASE = 'Passphrase' 45 46 def __init__(self, security='none'): 47 super(SecurityConfig, self).__init__() 48 self.security = security 49 50 51 def get_hostapd_config(self): 52 """@return dict fragment of hostapd configuration for security.""" 53 return {} 54 55 56 def get_shill_service_properties(self): 57 """@return dict of shill service properties.""" 58 return {} 59 60 61 def get_wpa_cli_properties(self): 62 """@return dict values to be set with wpa_cli set_network.""" 63 return {'key_mgmt': 'NONE'} 64 65 66 def install_router_credentials(self, host, install_dir): 67 """Install the necessary credentials on the router. 68 69 @param host host object representing the router. 70 @param install_dir the directory on host to install the files. 71 72 """ 73 pass # Many authentication methods have no special router credentials. 74 75 76 def install_client_credentials(self, tpm_store): 77 """Install credentials on the local host (hopefully a DUT). 78 79 Only call this if we're running on a DUT in a WiFi test. This 80 method can do things like install credentials into the TPM. 81 82 @param tpm_store TPMStore object representing the TPM on our DUT. 83 84 """ 85 pass # Many authentication methods have no special client credentials. 86 87 88 def __repr__(self): 89 return '%s(%s)' % (self.__class__.__name__, ', '.join( 90 ['%s=%r' % item for item in six.iteritems(vars(self))])) 91 92 93class WEPConfig(SecurityConfig): 94 """Abstracts security configuration for a WiFi network using static WEP.""" 95 # Open system authentication means that we don't do a 4 way AUTH handshake, 96 # and simply start using the WEP keys after association finishes. 97 AUTH_ALGORITHM_OPEN = 1 98 # This refers to a mode where the AP sends a plaintext challenge and the 99 # client sends back the challenge encrypted with the WEP key as part of a 4 100 # part auth handshake. 101 AUTH_ALGORITHM_SHARED = 2 102 AUTH_ALGORITHM_DEFAULT = AUTH_ALGORITHM_OPEN 103 104 @staticmethod 105 def _format_key(key, ascii_key_formatter): 106 """Returns a key formatted to for its appropriate consumer. 107 108 Both hostapd and wpa_cli want their ASCII encoded WEP keys formatted 109 in a particular way. Hex string on the other hand can be given raw. 110 Other key formats aren't even accepted, and this method will raise 111 and exception if it sees such a key. 112 113 @param key string a 40/104 bit WEP key. 114 @param ascii_key_formatter converter function that escapes a WEP 115 string-encoded passphrase. This conversion varies in format 116 depending on the consumer. 117 @return string corrected formatted WEP key. 118 119 """ 120 if len(key) in (5, 13): 121 # These are 'ASCII' strings, or at least N-byte strings 122 # of the right size. 123 return ascii_key_formatter(key) 124 125 if len(key) in (10, 26): 126 # These are hex encoded byte strings. 127 return key 128 129 raise error.TestFail('Invalid WEP key: %r' % key) 130 131 132 def __init__(self, wep_keys, wep_default_key=0, 133 auth_algorithm=AUTH_ALGORITHM_DEFAULT): 134 """Construct a WEPConfig object. 135 136 @param wep_keys list of string WEP keys. 137 @param wep_default_key int 0 based index into |wep_keys| for the default 138 key. 139 @param auth_algorithm int bitfield of AUTH_ALGORITHM_* defined above. 140 141 """ 142 super(WEPConfig, self).__init__(security='wep') 143 self.wep_keys = wep_keys 144 self.wep_default_key = wep_default_key 145 self.auth_algorithm = auth_algorithm 146 if self.auth_algorithm & ~(self.AUTH_ALGORITHM_OPEN | 147 self.AUTH_ALGORITHM_SHARED): 148 raise error.TestFail('Invalid authentication mode specified (%d).' % 149 self.auth_algorithm) 150 151 if self.wep_keys and len(self.wep_keys) > 4: 152 raise error.TestFail('More than 4 WEP keys specified (%d).' % 153 len(self.wep_keys)) 154 155 156 def get_hostapd_config(self): 157 """@return dict fragment of hostapd configuration for security.""" 158 ret = {} 159 quote = lambda x: '"%s"' % x 160 for idx,key in enumerate(self.wep_keys): 161 ret['wep_key%d' % idx] = self._format_key(key, quote) 162 ret['wep_default_key'] = self.wep_default_key 163 ret['auth_algs'] = self.auth_algorithm 164 return ret 165 166 167 def get_shill_service_properties(self): 168 """@return dict of shill service properties.""" 169 return {self.SERVICE_PROPERTY_PASSPHRASE: '%d:%s' % ( 170 self.wep_default_key, 171 self.wep_keys[self.wep_default_key])} 172 173 174 def get_wpa_cli_properties(self): 175 properties = super(WEPConfig, self).get_wpa_cli_properties() 176 quote = lambda x: '\\"%s\\"' % x 177 for idx, key in enumerate(self.wep_keys): 178 properties['wep_key%d' % idx] = self._format_key(key, quote) 179 properties['wep_tx_keyidx'] = self.wep_default_key 180 if self.auth_algorithm == self.AUTH_ALGORITHM_SHARED: 181 properties['auth_alg'] = 'SHARED' 182 return properties 183 184 185class WPAConfig(SecurityConfig): 186 """Abstracts security configuration for a WPA encrypted WiFi network.""" 187 188 # We have the option of turning on WPA, WPA2, or both via a bitfield. 189 MODE_PURE_WPA = 1 190 MODE_PURE_WPA2 = 2 191 MODE_MIXED_WPA = MODE_PURE_WPA | MODE_PURE_WPA2 192 MODE_DEFAULT = MODE_MIXED_WPA 193 194 # WPA2 mandates the use of AES in CCMP mode. 195 # WPA allows the use of 'ordinary' AES, but mandates support for TKIP. 196 # The protocol however seems to indicate that you just list a bunch of 197 # different ciphers that you support and we'll start speaking one. 198 CIPHER_CCMP = 'CCMP' 199 CIPHER_TKIP = 'TKIP' 200 201 # Fast Transition (FT) mode for WPA network. 202 FT_MODE_NONE = 1 203 FT_MODE_PURE = 2 204 FT_MODE_MIXED = FT_MODE_NONE | FT_MODE_PURE 205 FT_MODE_DEFAULT = FT_MODE_NONE 206 207 def __init__(self, psk='', wpa_mode=MODE_DEFAULT, wpa_ciphers=[], 208 wpa2_ciphers=[], wpa_ptk_rekey_period=None, 209 wpa_gtk_rekey_period=None, wpa_gmk_rekey_period=None, 210 use_strict_rekey=None, ft_mode=FT_MODE_NONE): 211 """Construct a WPAConfig. 212 213 @param psk string a passphrase (64 hex characters or an ASCII phrase up 214 to 63 characters long). 215 @param wpa_mode int one of MODE_* above. 216 @param wpa_ciphers list of ciphers to advertise in the WPA IE. 217 @param wpa2_ciphers list of ciphers to advertise in the WPA2 IE. 218 hostapd will fall back on WPA ciphers for WPA2 if this is 219 left unpopulated. 220 @param wpa_ptk_rekey_period int number of seconds between PTK rekeys. 221 @param wpa_gtk_rekey_period int number of second between GTK rekeys. 222 @param wpa_gmk_rekey_period int number of seconds between GMK rekeys. 223 The GMK is a key internal to hostapd used to generate GTK. 224 It is the 'master' key. 225 @param use_strict_rekey bool True iff hostapd should refresh the GTK 226 whenever any client leaves the group. 227 @param ft_mode int one of the FT_MODE_* in SecurityConfig. 228 229 """ 230 super(WPAConfig, self).__init__(security='psk') 231 self.psk = psk 232 self.wpa_mode = wpa_mode 233 self.wpa_ciphers = wpa_ciphers 234 self.wpa2_ciphers = wpa2_ciphers 235 self.wpa_ptk_rekey_period = wpa_ptk_rekey_period 236 self.wpa_gtk_rekey_period = wpa_gtk_rekey_period 237 self.wpa_gmk_rekey_period = wpa_gmk_rekey_period 238 self.use_strict_rekey = use_strict_rekey 239 self.ft_mode = ft_mode 240 if len(psk) > 64: 241 raise error.TestFail('WPA passphrases can be no longer than 63 ' 242 'characters (or 64 hex digits).') 243 244 if len(psk) == 64: 245 for c in psk: 246 if c not in '0123456789abcdefABCDEF': 247 raise error.TestFail('Invalid PMK: %r' % psk) 248 249 250 def get_hostapd_config(self): 251 """@return dict fragment of hostapd configuration for security.""" 252 if not self.wpa_mode: 253 raise error.TestFail('Cannot configure WPA unless we know which ' 254 'mode to use.') 255 256 if self.MODE_PURE_WPA & self.wpa_mode and not self.wpa_ciphers: 257 raise error.TestFail('Cannot configure WPA unless we know which ' 258 'ciphers to use.') 259 260 if not self.wpa_ciphers and not self.wpa2_ciphers: 261 raise error.TestFail('Cannot configure WPA2 unless we have some ' 262 'ciphers.') 263 264 ret = {'wpa': self.wpa_mode, 265 'wpa_key_mgmt': 'WPA-PSK'} 266 if self.ft_mode == self.FT_MODE_PURE: 267 ret['wpa_key_mgmt'] = 'FT-PSK' 268 elif self.ft_mode == self.FT_MODE_MIXED: 269 ret['wpa_key_mgmt'] = 'WPA-PSK FT-PSK' 270 if len(self.psk) == 64: 271 ret['wpa_psk'] = self.psk 272 else: 273 ret['wpa_passphrase'] = self.psk 274 275 if self.wpa_ciphers: 276 ret['wpa_pairwise'] = ' '.join(self.wpa_ciphers) 277 if self.wpa2_ciphers: 278 ret['rsn_pairwise'] = ' '.join(self.wpa2_ciphers) 279 if self.wpa_ptk_rekey_period: 280 ret['wpa_ptk_rekey'] = self.wpa_ptk_rekey_period 281 if self.wpa_gtk_rekey_period: 282 ret['wpa_group_rekey'] = self.wpa_gtk_rekey_period 283 if self.wpa_gmk_rekey_period: 284 ret['wpa_gmk_rekey'] = self.wpa_gmk_rekey_period 285 if self.use_strict_rekey: 286 ret['wpa_strict_rekey'] = 1 287 return ret 288 289 290 def get_shill_service_properties(self): 291 """@return dict of shill service properties.""" 292 ret = {self.SERVICE_PROPERTY_PASSPHRASE: self.psk} 293 return ret 294 295 296 def get_wpa_cli_properties(self): 297 properties = super(WPAConfig, self).get_wpa_cli_properties() 298 # TODO(wiley) This probably doesn't work for raw PMK. 299 protos = [] 300 if self.wpa_mode & self.MODE_PURE_WPA: 301 protos.append('WPA') 302 if self.wpa_mode & self.MODE_PURE_WPA2: 303 protos.append('RSN') 304 properties.update({'psk': '\\"%s\\"' % self.psk, 305 'key_mgmt': 'WPA-PSK', 306 'proto': ' '.join(protos)}) 307 if self.ft_mode == self.FT_MODE_PURE: 308 properties['key_mgmt'] = 'FT-PSK' 309 elif self.ft_mode == self.FT_MODE_MIXED: 310 properties['key_mgmt'] = 'WPA-PSK FT-PSK' 311 return properties 312 313 314class EAPConfig(SecurityConfig): 315 """Abstract superclass that implements certificate/key installation.""" 316 317 DEFAULT_EAP_USERS = '* TLS' 318 DEFAULT_EAP_IDENTITY = 'chromeos' 319 320 SERVICE_PROPERTY_CA_CERT_PEM = 'EAP.CACertPEM' 321 SERVICE_PROPERTY_CLIENT_CERT_ID = 'EAP.CertID' 322 SERVICE_PROPERTY_EAP_IDENTITY = 'EAP.Identity' 323 SERVICE_PROPERTY_EAP_KEY_MGMT = 'EAP.KeyMgmt' 324 SERVICE_PROPERTY_EAP_PASSWORD = 'EAP.Password' 325 SERVICE_PROPERTY_EAP_PIN = 'EAP.PIN' 326 SERVICE_PROPERTY_INNER_EAP= 'EAP.InnerEAP' 327 SERVICE_PROPERTY_PRIVATE_KEY_ID = 'EAP.KeyID' 328 SERVICE_PROPERTY_USE_SYSTEM_CAS = 'EAP.UseSystemCAs' 329 SERVICE_PROPERTY_ALTSUBJECT_MATCH = 'EAP.SubjectAlternativeNameMatch' 330 331 last_tpm_id = 8800 332 333 # Credential file prefixes. 334 SERVER_CA_CERT_FILE_PREFIX = 'hostapd_ca_cert_file.' 335 SERVER_CERT_FILE_PREFIX = 'hostapd_cert_file.' 336 SERVER_KEY_FILE_PREFIX = 'hostapd_key_file.' 337 SERVER_EAP_USER_FILE_PREFIX = 'hostapd_eap_user_file.' 338 339 @staticmethod 340 def reserve_TPM_id(): 341 """@return session unique TPM identifier.""" 342 ret = str(EAPConfig.last_tpm_id) 343 EAPConfig.last_tpm_id += 1 344 return ret 345 346 347 def __init__(self, security='802_1x', file_suffix=None, use_system_cas=None, 348 server_ca_cert=None, server_cert=None, server_key=None, 349 server_eap_users=None, 350 client_ca_cert=None, client_cert=None, client_key=None, 351 client_cert_id=None, client_key_id=None, 352 eap_identity=None, ft_mode=WPAConfig.FT_MODE_DEFAULT, 353 altsubject_match=None): 354 """Construct an EAPConfig. 355 356 @param file_suffix string unique file suffix on DUT. 357 @param use_system_cas False iff we should ignore server certificates. 358 @param server_ca_cert string PEM encoded CA certificate for the server. 359 @param server_cert string PEM encoded identity certificate for server. 360 @param server_key string PEM encoded private key for server. 361 @param server_eap_users string contents of EAP user file. 362 @param client_ca_cert string PEM encoded CA certificate for client. 363 @param client_cert string PEM encoded identity certificate for client. 364 @param client_key string PEM encoded private key for client. 365 @param client_cert_id string identifier for client certificate in TPM. 366 @param client_key_id string identifier for client private key in TPM. 367 @param eap_identity string user to authenticate as during EAP. 368 @param ft_mode int one of the FT_MODE_* in SecurityConfig. 369 @param altsubject_match list of strings in the format of shill 370 EAP.SubjectAlternativeNameMatch property. 371 372 """ 373 super(EAPConfig, self).__init__(security=security) 374 self.use_system_cas = use_system_cas 375 self.server_ca_cert = server_ca_cert 376 self.server_cert = server_cert 377 self.server_key = server_key 378 self.server_eap_users = server_eap_users or self.DEFAULT_EAP_USERS 379 self.client_ca_cert = client_ca_cert 380 self.client_cert = client_cert 381 self.client_key = client_key 382 if file_suffix is None: 383 suffix_letters = string.ascii_lowercase + string.digits 384 file_suffix = ''.join(random.choice(suffix_letters) 385 for x in range(10)) 386 logging.debug('Choosing unique file_suffix %s.', file_suffix) 387 # The key paths will be determined in install_router_credentials. 388 self.server_ca_cert_file = None 389 self.server_cert_file = None 390 self.server_key_file = None 391 self.server_eap_user_file = None 392 # While these paths won't make it across the network, the suffix will. 393 self.file_suffix = file_suffix 394 self.client_cert_id = client_cert_id or self.reserve_TPM_id() 395 self.client_key_id = client_key_id or self.reserve_TPM_id() 396 # This gets filled in at install time. 397 self.pin = None 398 # The slot where the certificate/key are installed in the TPM. 399 self.client_cert_slot_id = None 400 self.client_key_slot_id = None 401 self.eap_identity = eap_identity or self.DEFAULT_EAP_IDENTITY 402 self.ft_mode = ft_mode 403 self.altsubject_match = altsubject_match 404 405 406 def install_router_credentials(self, host, install_dir): 407 """Install the necessary credentials on the router. 408 409 @param host host object representing the router. 410 411 """ 412 self.server_ca_cert_file = os.path.join( 413 install_dir, self.SERVER_CA_CERT_FILE_PREFIX + self.file_suffix) 414 self.server_cert_file = os.path.join( 415 install_dir, self.SERVER_CERT_FILE_PREFIX + self.file_suffix) 416 self.server_key_file = os.path.join( 417 install_dir, self.SERVER_KEY_FILE_PREFIX + self.file_suffix) 418 self.server_eap_user_file = os.path.join( 419 install_dir, self.SERVER_EAP_USER_FILE_PREFIX + self.file_suffix) 420 421 files = [(self.server_ca_cert, self.server_ca_cert_file), 422 (self.server_cert, self.server_cert_file), 423 (self.server_key, self.server_key_file), 424 (self.server_eap_users, self.server_eap_user_file)] 425 for content, path in files: 426 # If we omit a parameter, just omit copying a file over. 427 if content is None: 428 continue 429 # Write the contents to local disk first so we can use the easy 430 # built in mechanism to do this. 431 with tempfile.NamedTemporaryFile() as f: 432 f.write(content) 433 f.flush() 434 os.chmod(f.name, stat.S_IRUSR | stat.S_IWUSR | 435 stat.S_IRGRP | stat.S_IWGRP | 436 stat.S_IROTH | stat.S_IWOTH) 437 host.send_file(f.name, path, delete_dest=True) 438 439 440 def install_client_credentials(self, tpm_store): 441 """Install credentials on the local host (hopefully a DUT). 442 443 Only call this if we're running on a DUT in a WiFi test. This 444 method can do things like install credentials into the TPM. 445 446 @param tpm_store TPMStore object representing the TPM on our DUT. 447 448 """ 449 if self.client_cert: 450 tpm_store.install_certificate(self.client_cert, self.client_cert_id) 451 self.client_cert_slot_id = tpm_store.SLOT_ID 452 self.pin = tpm_store.PIN 453 if self.client_key: 454 tpm_store.install_private_key(self.client_key, self.client_key_id) 455 self.client_key_slot_id = tpm_store.SLOT_ID 456 self.pin = tpm_store.PIN 457 458 459 def get_shill_service_properties(self): 460 """@return dict of shill service properties.""" 461 ret = {self.SERVICE_PROPERTY_EAP_IDENTITY: self.eap_identity} 462 if self.pin: 463 ret[self.SERVICE_PROPERTY_EAP_PIN] = self.pin 464 if self.client_ca_cert: 465 # Technically, we could accept a list of certificates here, but we 466 # have no such tests. 467 ret[self.SERVICE_PROPERTY_CA_CERT_PEM] = [self.client_ca_cert] 468 if self.client_cert: 469 ret[self.SERVICE_PROPERTY_CLIENT_CERT_ID] = ( 470 '%s:%s' % (self.client_cert_slot_id, self.client_cert_id)) 471 if self.client_key: 472 ret[self.SERVICE_PROPERTY_PRIVATE_KEY_ID] = ( 473 '%s:%s' % (self.client_key_slot_id, self.client_key_id)) 474 if self.use_system_cas is not None: 475 ret[self.SERVICE_PROPERTY_USE_SYSTEM_CAS] = self.use_system_cas 476 if self.altsubject_match: 477 ret[self.SERVICE_PROPERTY_ALTSUBJECT_MATCH] = self.altsubject_match 478 return ret 479 480 481 def get_hostapd_config(self): 482 """@return dict fragment of hostapd configuration for security.""" 483 return {'ieee8021x': 1, # Enable 802.1x support. 484 'eap_server' : 1, # Do EAP inside hostapd to avoid RADIUS. 485 'ca_cert': self.server_ca_cert_file, 486 'server_cert': self.server_cert_file, 487 'private_key': self.server_key_file, 488 'eap_user_file': self.server_eap_user_file} 489 490 491class DynamicWEPConfig(EAPConfig): 492 """Configuration settings bundle for dynamic WEP. 493 494 This is a WEP encrypted connection where the keys are negotiated after the 495 client authenticates via 802.1x. 496 497 """ 498 499 DEFAULT_REKEY_PERIOD = 20 500 501 502 def __init__(self, use_short_keys=False, 503 wep_rekey_period=DEFAULT_REKEY_PERIOD, 504 server_ca_cert=None, server_cert=None, server_key=None, 505 client_ca_cert=None, client_cert=None, client_key=None, 506 file_suffix=None, client_cert_id=None, client_key_id=None): 507 """Construct a DynamicWEPConfig. 508 509 @param use_short_keys bool force hostapd to use 40 bit WEP keys. 510 @param wep_rekey_period int number of second between rekeys. 511 @param server_ca_cert string PEM encoded CA certificate for the server. 512 @param server_cert string PEM encoded identity certificate for server. 513 @param server_key string PEM encoded private key for server. 514 @param client_ca_cert string PEM encoded CA certificate for client. 515 @param client_cert string PEM encoded identity certificate for client. 516 @param client_key string PEM encoded private key for client. 517 @param file_suffix string unique file suffix on DUT. 518 @param client_cert_id string identifier for client certificate in TPM. 519 @param client_key_id string identifier for client private key in TPM. 520 521 """ 522 super(DynamicWEPConfig, self).__init__( 523 security='wep', file_suffix=file_suffix, 524 server_ca_cert=server_ca_cert, server_cert=server_cert, 525 server_key=server_key, client_ca_cert=client_ca_cert, 526 client_cert=client_cert, client_key=client_key, 527 client_cert_id=client_cert_id, client_key_id=client_key_id) 528 self.use_short_keys = use_short_keys 529 self.wep_rekey_period = wep_rekey_period 530 531 532 def get_hostapd_config(self): 533 """@return dict fragment of hostapd configuration for security.""" 534 ret = super(DynamicWEPConfig, self).get_hostapd_config() 535 key_len = 13 # 128 bit WEP, 104 secret bits. 536 if self.use_short_keys: 537 key_len = 5 # 64 bit WEP, 40 bits of secret. 538 ret.update({'wep_key_len_broadcast': key_len, 539 'wep_key_len_unicast': key_len, 540 'wep_rekey_period': self.wep_rekey_period}) 541 return ret 542 543 544 def get_shill_service_properties(self): 545 """@return dict of shill service properties.""" 546 ret = super(DynamicWEPConfig, self).get_shill_service_properties() 547 ret.update({self.SERVICE_PROPERTY_EAP_KEY_MGMT: 'IEEE8021X'}) 548 return ret 549 550 551class WPAEAPConfig(EAPConfig): 552 """Security type to set up a WPA tunnel via EAP-TLS negotiation.""" 553 554 def __init__(self, file_suffix=None, use_system_cas=None, 555 server_ca_cert=None, server_cert=None, server_key=None, 556 client_ca_cert=None, client_cert=None, client_key=None, 557 client_cert_id=None, client_key_id=None, eap_identity=None, 558 server_eap_users=None, altsubject_match=None, 559 wpa_mode=WPAConfig.MODE_PURE_WPA, 560 ft_mode=WPAConfig.FT_MODE_DEFAULT): 561 """Construct a DynamicWEPConfig. 562 563 @param file_suffix string unique file suffix on DUT. 564 @param use_system_cas False iff we should ignore server certificates. 565 @param server_ca_cert string PEM encoded CA certificate for the server. 566 @param server_cert string PEM encoded identity certificate for server. 567 @param server_key string PEM encoded private key for server. 568 @param client_ca_cert string PEM encoded CA certificate for client. 569 @param client_cert string PEM encoded identity certificate for client. 570 @param client_key string PEM encoded private key for client. 571 @param client_cert_id string identifier for client certificate in TPM. 572 @param client_key_id string identifier for client private key in TPM. 573 @param eap_identity string user to authenticate as during EAP. 574 @param server_eap_users string contents of server EAP users file. 575 @param ft_mode int one of the FT_MODE_* in SecurityConfig 576 @param altsubject_match list of strings in the format of shill 577 EAP.SubjectAlternativeNameMatch property. 578 579 """ 580 super(WPAEAPConfig, self).__init__( 581 file_suffix=file_suffix, use_system_cas=use_system_cas, 582 server_ca_cert=server_ca_cert, server_cert=server_cert, 583 server_key=server_key, client_ca_cert=client_ca_cert, 584 client_cert=client_cert, client_key=client_key, 585 client_cert_id=client_cert_id, client_key_id=client_key_id, 586 eap_identity=eap_identity, server_eap_users=server_eap_users, 587 ft_mode=ft_mode, altsubject_match=altsubject_match) 588 self.wpa_mode = wpa_mode 589 590 591 def get_hostapd_config(self): 592 """@return dict fragment of hostapd configuration for security.""" 593 ret = super(WPAEAPConfig, self).get_hostapd_config() 594 # If we wanted to expand test coverage to WPA2/PEAP combinations 595 # or particular ciphers, we'd have to let people set these 596 # settings manually. But for now, do the simple thing. 597 ret.update({'wpa': self.wpa_mode, 598 'wpa_pairwise': WPAConfig.CIPHER_CCMP, 599 'wpa_key_mgmt':'WPA-EAP'}) 600 if self.ft_mode == WPAConfig.FT_MODE_PURE: 601 ret['wpa_key_mgmt'] = 'FT-EAP' 602 elif self.ft_mode == WPAConfig.FT_MODE_MIXED: 603 ret['wpa_key_mgmt'] = 'WPA-EAP FT-EAP' 604 return ret 605 606 607class Tunneled1xConfig(WPAEAPConfig): 608 """Security type to set up a TTLS/PEAP connection. 609 610 Both PEAP and TTLS are tunneled protocols which use EAP inside of a TLS 611 secured tunnel. The secured tunnel is a symmetric key encryption scheme 612 negotiated under the protection of a public key in the server certificate. 613 Thus, we'll see server credentials in the form of certificates, but client 614 credentials in the form of passwords and a CA Cert to root the trust chain. 615 616 """ 617 618 TTLS_PREFIX = 'TTLS-' 619 620 LAYER1_TYPE_PEAP = 'PEAP' 621 LAYER1_TYPE_TTLS = 'TTLS' 622 623 LAYER2_TYPE_GTC = 'GTC' 624 LAYER2_TYPE_MSCHAPV2 = 'MSCHAPV2' 625 LAYER2_TYPE_MD5 = 'MD5' 626 LAYER2_TYPE_TTLS_MSCHAPV2 = TTLS_PREFIX + 'MSCHAPV2' 627 LAYER2_TYPE_TTLS_MSCHAP = TTLS_PREFIX + 'MSCHAP' 628 LAYER2_TYPE_TTLS_PAP = TTLS_PREFIX + 'PAP' 629 630 def __init__(self, server_ca_cert, server_cert, server_key, 631 client_ca_cert, eap_identity, password, 632 outer_protocol=LAYER1_TYPE_PEAP, 633 inner_protocol=LAYER2_TYPE_MD5, 634 client_password=None, file_suffix=None, 635 altsubject_match=None): 636 self.password = password 637 if client_password is not None: 638 # Override the password used on the client. This lets us set 639 # bad passwords for testing. However, we use the real password 640 # below for the server config. 641 self.password = client_password 642 self.inner_protocol = inner_protocol 643 # hostapd wants these surrounded in double quotes. 644 quote = lambda x: '"' + x + '"' 645 eap_users = list(map(' '.join, [('*', outer_protocol), 646 (quote(eap_identity), inner_protocol, quote(password), '[2]')])) 647 super(Tunneled1xConfig, self).__init__( 648 server_ca_cert=server_ca_cert, 649 server_cert=server_cert, 650 server_key=server_key, 651 server_eap_users='\n'.join(eap_users), 652 client_ca_cert=client_ca_cert, 653 eap_identity=eap_identity, 654 file_suffix=file_suffix, 655 altsubject_match=altsubject_match) 656 657 658 def get_shill_service_properties(self): 659 """@return dict of shill service properties.""" 660 ret = super(Tunneled1xConfig, self).get_shill_service_properties() 661 ret.update({self.SERVICE_PROPERTY_EAP_PASSWORD: self.password}) 662 if self.inner_protocol.startswith(self.TTLS_PREFIX): 663 auth_str = 'auth=' + self.inner_protocol[len(self.TTLS_PREFIX):] 664 ret.update({self.SERVICE_PROPERTY_INNER_EAP: auth_str}) 665 return ret 666