Skip to content

Instantly share code, notes, and snippets.

@joshua-d-miller
Last active January 18, 2019 12:46
Show Gist options
  • Save joshua-d-miller/241cd83602cc33882ff9ab54ab92e425 to your computer and use it in GitHub Desktop.
Save joshua-d-miller/241cd83602cc33882ff9ab54ab92e425 to your computer and use it in GitHub Desktop.
When deploying an EAP-TLS profile for 802.1x Ethernet, this script will create a User Identity preference which will allow a logged in user to use any Ethernet interface that is connected to their machine.
#!/usr/bin/python
# pylint: disable=C0103, W0612, E1101, E0602, E0611
# pylint: disable=W0101, W0110, W0141
''''Fix 802.1x When Using Config Profiles and EAP-TLS'''
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# This script will determine the profile ID that was used when
# installing the 802.1x Ethernet Identity preference and make
# a copy of the keychain entry to be used as a user identity preference
# so that any Ethernet can be used when a user is logged in. As of 10.12.3
# there is currently a bug with the 802.1x System profile that will only
# allow the first Ethernet that was active at the time of profile install
# to be used.
#
#
# Joshua D. Miller - josh@psu.edu - The Pennsylvania State University
# Last Updated Friday February 23, 2016
#
# Many Many Thanks to Michael Lynn and Mosen for taking me down
# the rabbit hole that is PyObjC
#
# References
# https://gist.github.com/pudquick/8ad859f30438f3be149fe9751391d037
# https://gist.github.com/pudquick/4cf32c2b403bf0be23b268d6dd3cf803
# https://github.com/mosen/pudquick-pyobjc-framework-Security/
# blob/master/metadata/raw/x86_64-10.12.fwinfo
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Steps Performed in this script
# - Determine Profile ID by reading /Library/Preferences/
# SystemConfiguration
# /com.apple.network.eapolclient.configuration.plist
# - Find the keycahin entry
# com.apple.network.eap.system.identity.profileid.UUID
# - Create a copy named
# com.apple.network.eap.user.identity.profileid.UUID
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Most of EAP 802.1x and Keychain PyObjC code from Michael Lynn
# Instructions on using SecIdentityCopyPreferred and SecIdentityPreferred
# provided by Mosen
from __future__ import print_function
from Foundation import NSBundle
import objc
def get_profile_uuid():
'''Get the UUID of the Configuration Profiles 802.1x Identity
Preference'''
EAP8021X_bundle = NSBundle.bundleWithPath_(
'/System/Library/PrivateFrameworks/EAP8021X.framework')
E_functions = [
('EAPOLControlCopyStateAndStatus', 'i*o^io^@'),
('EAPOLClientConfigurationCreate', '@^@'),
('EAPOLClientConfigurationCopyProfiles', '@@'),
('EAPOLClientProfileGetUserDefinedName', '@@'),
('EAPOLClientProfileGetID', '@@'), ]
objc.loadBundleFunctions(EAP8021X_bundle, globals(), E_functions)
maximum_interfaces = 20
interfaces = map(lambda x: 'en%s' % x, range(maximum_interfaces))
for i in interfaces:
result, status, eap_config = EAPOLControlCopyStateAndStatus(
i, None, None)
if result == 0:
cfg = EAPOLClientConfigurationCreate(None)
profiles = EAPOLClientConfigurationCopyProfiles(cfg)
for n in profiles:
display_name = EAPOLClientProfileGetUserDefinedName(n)
if display_name == 'Ethernet - CoE':
profile_id = EAPOLClientProfileGetID(n)
return profile_id
def create_identity_preference():
'''Time to modify the keychain and create the identity preference we need
to fix Apple's issue - Still an issue as of 10.12.3'''
# from CoreFoundation import CFGetTypeID
Security_framework = NSBundle.bundleWithPath_(
'/System/Library/Frameworks/Security.framework')
# Objective C Functions we will use to accomplish our keychain
# change
keychain_functions = [
('SecKeychainGetTypeID', 'I'),
('SecIdentityGetTypeID', 'I'),
('SecKeychainSetUserInteractionAllowed', 'IZ'),
('SecKeychainCopyDomainDefault', 'IIo^^{__CFArray}'),
('SecKeychainOpen', 'I*o^^{__SecKeychain=}'),
('SecKeychainGetStatus', 'I^{__SecKeychain=}o^I'),
('SecKeychainUnlock', 'I^{__SecKeychain=}I*I'),
('SecIdentityCopyPreferred',
'^{__SecIdentityRef=}^{__CFString}^{__CFArray}^{__CFArray}'),
('SecKeychainSetPreferenceDomain', 'II'),
('SecIdentitySetPreferred',
'I^{__SecIdentityRef=}^{__CFString}^{__CFArray}'),
('SecKeychainLock', 'I^{__SecKeychain=}'), ]
objc.loadBundleFunctions(Security_framework, globals(), keychain_functions)
# Build out our CF type signatures
objc.registerCFSignature(
'SecKeychainRef', '^{__SecKeychain=}', SecKeychainGetTypeID())
objc.registerCFSignature(
'SecIdentityRef', '^{__SecIdentityRef=}', SecIdentityGetTypeID())
kSecUnlockStateStatus = 1
kSecPreferencesDomainSystem = 1
system_profile_name = ('com.apple.network.eap.system'
'.identity.profileid.{0:}'.format(uuid))
user_profile_name = ('com.apple.network.eap.user'
'.identity.profileid.{0:}'.format(uuid))
# Turn off any prompts for a user to enter a password for keychain
SecKeychainSetUserInteractionAllowed(False)
# Let's Open the System Keychain
err, keychains = SecKeychainCopyDomainDefault(
kSecPreferencesDomainSystem, None)
err, keychain = SecKeychainOpen('System.keychain', None)
if err:
return err
# Check Status of the System Keychain (Should be locked)
err, status = SecKeychainGetStatus(keychain, None)
unlocked = status | kSecUnlockStateStatus == kSecUnlockStateStatus
if not unlocked:
# Unlock the System Keychain
keychain = SecKeychainUnlock(keychain, 0, None, False)
# Copy our Identity Preference and make the new one with
# the same attributes
keychain_item = SecIdentityCopyPreferred(
system_profile_name, None, None)
# Apply Change to the System Keychain
SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem)
err = SecIdentitySetPreferred(keychain_item, user_profile_name, None)
if err:
return err
# Lock the System Keychain
err = SecKeychainLock(keychain)
return None
if __name__ == '__main__':
uuid = get_profile_uuid()
fin_code = create_identity_preference()
if fin_code is not None:
# Build the SecCopyErrorMessageString to read the error
Security = NSBundle.bundleWithPath_(
'/System/Library/Frameworks/Security.framework')
functions = [
('SecCopyErrorMessageString', '^{__CFString}I^v'), ]
objc.loadBundleFunctions(Security, globals(), functions)
message = SecCopyErrorMessageString(fin_code, None)
print("Error: {}".format(message))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment