Overview
In enterprise environments, access to a wireless (or wired) network can be secured using 802.1X and X.509 certificates. The process that provides access on macOS, eapolclient, can be configured either via an MDM configuration profile or directly on the macOS client.
The MDM configuration profile allows for three different ways to provide the X.509 certificate to the client: SCEP, p12, and the AD Certificate Profile. This may not be a good fit for every organization. If an organization does not use SCEP to sign certificate requests, requires that the private key not be made available outside the client as a .p12 file, or does not bind Macs to Active Directory (a requirement for the AD Certificate profile), the Mac 802.1X configuration cannot be done with a configuration profile. It can, however, be configured on the Mac without a X.509 configuration profile.
Twocanoes Software has a solution named Certificate Request that enables a Mac to generate a private key locally on the Mac, submit a certificate signing request natively to a Microsoft Certificate Authority using DCE/RPCs, and install the certificate into the keychain. The certificate can be either a machine or a user certificate.
Since the private key is not created or installed by an Apple process, the keychain permissions (called an Access Control List, or ACL) must be set up correctly in order to avoid unnecessary user prompting. Over the past few weeks, we have been working on Certificate Request to configure EAP-TLS without prompting the user. Recent changes in the macOS login keychain required understanding why the prompting was occurring and investigating different options for eliminating these prompts.
Resources
Keychain Detective: https://bitbucket.org/twocanoes/keychain-detective/downloads/
eapolcfg (binary is included in Keychain Detective in Contents/MacOS as well): https://bitbucket.org/twocanoes/eapolcfg/src/main/
Eap8021x client from Apple (no longer available)
Security from Apple (no longer available)
The Problem
In our test lab, we have an 802.1X setup to test certificate-based authentication to wireless networks. When you attempt to join an 802.1X authenticated wireless on macOS Big Sur, the user is then prompted to select a username and password.
Since this is certificate-based, the user needs to select EAP-TLS, then select the appropriate identity associated with the wireless network.
When joining the network for the first time, the client needs to authenticate to the RADIUS server, so a trust dialog is shown. This dialog looks like a standard trust dialog, but if you read the text carefully, it reads “examine the server’s certificate to ensure that it is appropriate for this network.” Since a wireless network SSID can be broadcast by any access point, the URL for authenticating to the network is not known and must be explicitly trusted. This trust is saved in a user preference when the user clicks continue, which is discussed below.
The prompt also has an entry for username, though the username may or may not be required (in our case, it wasn’t). Once the “Join” button is clicked, the user is prompted multiple times to enter in their login password. These prompts allow access to the private key associated with the certificate and set up permissions on the keychain items.
In order to streamline the process, reduce user confusion, and help desk traffic, prompting the user for unknown items and entering the login password had to be eliminated.
If you want to know how to solve the problem of prompting, skip to the “Avoiding Prompting by using the System Keychain” section. We investigated why the prompting occurred and gained a better understanding of the changes in the login keychain. In short, it didn’t allow us to use the login keychain without prompting. As a result, we have escalated the issue with Apple and hope to resolve this issue in the future.
Using the Login Keychain with Access Controls and Identity Preferences
To eliminate the trust dialog for the RADIUS server, a user preference can be set for the wireless network with an SHA-1 hash of the certificate as a key and the certificate as the value. For example:
defaults read com.apple.network.eapclient.tls.TrustExceptions { WirelessSSID = { OneEx = { 1f9575a7fdce56ab5461e89c0738841c94f02c81 = {length = 83, bytes = 0x62706c69 73743030 a101d102 035a5348 ... 00000000 0000002f }; }; }; }
Alternatively, an EAPoL configuration profile can be set. This is a set of configurations for 802.1X stored in /Library/Preferences/SystemConfiguration/com.apple.network.eapolclient.configuration.plist. This file is readable and writable only by root. A tool for editing this configuration is available at opensource.apple.com; a compiled version is available at Twocanoes Software.
For example, to create a configuration that trusts the web.twocanoes.com RADIUS server name for EAP-TLS (authType 13) to wireless SSID “OneEx” using any security type (WEP, WPA, or WPA2):
./eapolcfg createProfile --SSID OneEx --userDefinedName "TCS EAPOL Profile" --trustedServerName "web.twocanoes.com" --authType 13 --securityType Any
This configuration will be shown in System Preferences as well:
To eliminate the prompt to select the correct client certificate, an identity preference can be created in the macOS keychain associating the certificate with the wireless name (SSID). The identity preference must be named “com.apple.network.eap.user.identity.wlan.ssid.<SSID NAME>” with the correct certificate, then selected in the identity preference.
Now, the EAPoL client knows that the wireless network named “OneEx” (a wireless network and SSID I created) is associated with the login keychain identity “tcadmin”. This identity was created in the login keychain by our Certificate Request from the Windows Certificate Authority, but it could be any valid 802.1X X.509 identity.
This resolves the issue of prompting for the certificate. However, the EAPoL client now prompts for access to the private key and prompts to update the identity preference.
A common way to avoid prompting the user is to set the Access Control on the private key. To accomplish this, the wireless network is accessed and the login password is entered when prompted. The private key’s Access Controls are then inspected to see what permissions are required. Then, the permissions can be set ahead of time to prevent user prompting.
In this case, after entering in the login password, there are two additional entries in the “Always allow access by these applications:”; these are eapolclient and AirPort (tcscertrequest was added when the identity was created in the login keychain).
Notice that the “AirPort” does not have a black icon, while eapolclient and tcscertrequest do. The eapolclient is located at:
/System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient
An AirPort binary was found in the AirPort.menu item, but this process was not the binary shown above. After adding in eapolclient, the prompts did not go away. Also, there is no way in Keychain Access to set an Access Control on an Identity Preferences.
To understand why the prompting still occurs, the issue was escalated via Developer Support and we participated in a customer escalation via AppleCare Enterprise support. We also collaborated with some great folks on Slack and developed custom tools to investigate the permission issues.
Login Keychain, Access Controls, iOS-type login keychain, and Partitions
macOS now has two different login keys: an iOS-style keychain and the standard macOS login keychain. Looking at the standard macOS login keychain, when a new item is created, it has three default Access Controls items: ACL 0, ACL 1, and ACL 2. Each item controls what applications are allowed to do with the private key. When a public and private key are created, these are the three Access Control Items created:
ACL 0: Authorizations: Authorization 0: ACLAuthorizationEncrypt Description: TCS Certificate Request Private Key Trusted Applications: All Applications Allowed (value is nil) ACL 1: Authorizations: Authorization 0: ACLAuthorizationDecrypt Authorization 1: ACLAuthorizationDerive Authorization 2: ACLAuthorizationExportClear Authorization 3: ACLAuthorizationExportWrapped Authorization 4: ACLAuthorizationMAC Authorization 5: ACLAuthorizationSign Description: TCS Certificate Request Private Key Trusted Applications: App 0: /Applications/Certificate Request.app/Contents/MacOS/tcscertrequest ACL 2: Authorizations: Authorization 0: ACLAuthorizationIntegrity Description: 3078d18ab22b2f207a8f41cfe8b89b6feaa820374044ad428c8ff75c6bd97ded Trusted Applications: All Applications Allowed (value is nil)
Each ACL item defines the operations that it protects and what applications are allowed to do these operations.
ACL 0 says that all applications are authorized for the ACLAuthorizationEncrypt operation.
The second ACL, ACL 1, authorizes that the process that created it, tcscertrequest, is allowed the follow operations without prompting:
- ACLAuthorizationDecrypt
- ACLAuthorizationDerive
- ACLAuthorizationExportClear
- ACLAuthorizationExportWrapped
- ACLAuthorizationMAC
- ACLAuthorizationSign.
The third ACL, ACL 2, says that all applications are authorized for the ACLAuthorizationIntegrity operation.
When an ACL item is created and then saved programmatically in macOS, a fourth ACL is added:
ACL 3: Authorizations: Authorization 0: ACLAuthorizationPartitionID Description: <hex data> Trusted Applications: All Applications Allowed (value is nil)
This ACL item, ACL 3, authorizes all applications to perform the ACLAuthorizationPartitionID operation on the item. The description is a hex encoded value of a property list. Decoding it gives the plist:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Partitions</key> <array> <string>teamid:UXP6YEHSPW</string> </array> </dict> </plist>
The Team ID in Partitions is the Team ID of the Twocanoes Software certificate that both signed the app and created the private key.
After attempting to connect to a network in the AirPort menu and allowing eapolclient access to the private key by providing the login password, two ACL items change.
ACL 1 adds in two additional applications:
ACL 1: Authorizations: Authorization 0: ACLAuthorizationDecrypt Authorization 1: ACLAuthorizationDerive Authorization 2: ACLAuthorizationExportClear Authorization 3: ACLAuthorizationExportWrapped Authorization 4: ACLAuthorizationMAC Authorization 5: ACLAuthorizationSign Description: TCS Certificate Request Private Key Trusted Applications: App 0: /Applications/Certificate Request.app/Contents/MacOS/tcscertrequest App 1: /System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient App 2: group://AirPort
Above, ACL 1 authorizes two additional applications (eapolclient and a special entry named “group://AirPort”) to perform ACLAuthorizationDecrypt, ACLAuthorizationDerive, ACLAuthorizationExportClear, ACLAuthorizationExportWrapped, ACLAuthorizationMAC, and ACLAuthorizationSign operations without prompting.
ACL 3 is also updated so the hex in the description now contain “apple:”
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Partitions</key> <array> <string>teamid:UXP6YEHSPW</string> <string>apple:</string> </array> </dict> </plist>
This explains why the AirPort ACL entry had a different icon. The AirPort entry isn’t an application but rather a keychain group called AirPort.
Keychain groups are a way that applications can share access to keychain items. It looks like the eapolclient added access to any applications that are part of the AirPort keychain keychain group. Typically, keychain groups have the Team ID of the developer in it, so this looks like a special keychain group for Apple.
The “apple:” key in the Partitions array means that any valid apple signed binary can access this keychain item (in this case, a private key).
However, there doesn’t seem to be a way to update this ACL item to add in apple: to the partition. There are a couple of different ways to update the ACLAuthorizationPartitionID ACL item, but all either fail or require the login password.
Security Command
The command line utility has the set-key-partition-list command, which can update the ACL ACLAuthorizationPartitionID key item. The set-generic-password-partition-list can update the identity preference. For example, to update a private key with the label TCSCertRequestKey and identity preference named “com.apple.network.eap.user.identity.wlan.ssid.OneEx”:
security set-key-partition-list -l TCSCertRequestKey -S "apple:" security set-generic-password-partition-list -l com.apple.network.eap.user.identity.wlan.ssid.OneEx -S "apple:"
Both commands prompt for the login password (or it can be specified with the -k option).
Programmatically Modifying the Access Item
UsUsing the security API, the kSecACLAuthorizationPartitionID ACL item can be retrieved, modified, and written back to the Access Control List item:
NSArray* array=(NSArray *)CFBridgingRelease(SecAccessCopyMatchingACLList(access, kSecACLAuthorizationPartitionID));
if (array && array.count>0){
CFArrayRef applicationList;
CFStringRef description;
SecKeychainPromptSelector promptSelector;
SecACLRef acl=(__bridge SecACLRef)[array objectAtIndex:0];
s= SecKeychainItemSetAccess(privateKey, access);
NSLog(@"%i",s);
OSStatus s=SecACLCopyContents(acl, &applicationList, &description, &promptSelector);
NSString *newDescription=@"3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c21444f435459504520706c697374205055424c494320222d2f2f4170706c652f2f44544420504c49535420312e302f2f454e222022687474703a2f2f7777772e6170706c652e636f6d2f445444732f50726f70657274794c6973742d312e302e647464223e0a3c706c6973742076657273696f6e3d22312e30223e0a3c646963743e0a093c6b65793e506172746974696f6e733c2f6b65793e0a093c61727261793e0a09093c737472696e673e7465616d69643a555850365945485350573c2f737472696e673e0a09093c737472696e673e6170706c653a3c2f737472696e673e0a093c2f61727261793e0a3c2f646963743e0a3c2f706c6973743e0a";
s=SecACLSetContents(acl, applicationList, (CFStringRef)newDescription, kSecKeychainPromptRequirePassphase);
NSLog(@"%i",s);
NSDictionary *attributesToUpdate=@{(id)kSecAttrAccess:(__bridge id)access};
// this fails with "invalid username or password" error
// s=SecKeychainItemSetAccess(privateKey, access);
// this works but you have to use the login password
s=SecKeychainItemSetAccessWithPassword(privateKey, access, 8, "password");
}
Create a new ACL Item and add kSecACLAuthorizationPartitionID before saving
When an ACL is created, it does not have a kSecACLAuthorizationPartitionID. Only after updating in the keychain does the Partition ID get added.
Also, before updating the ACL, an ACL item kSecACLAuthorizationPartitionID can be added. However, this is ignored; a new kSecACLAuthorizationPartitionID ACL item is added when the keychain is updated with the item. eapolclient still prompts and updates the ACL item added by the system.
CFArrayRef applicationList;
SecKeychainPromptSelector promptSelector=kSecKeychainPromptInvalid;
SecACLRef newAclEntry;
NSString *newDescription=@"3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c21444f435459504520706c697374205055424c494320222d2f2f4170706c652f2f44544420504c49535420312e302f2f454e222022687474703a2f2f7777772e6170706c652e636f6d2f445444732f50726f70657274794c6973742d312e302e647464223e0a3c706c6973742076657273696f6e3d22312e30223e0a3c646963743e0a093c6b65793e506172746974696f6e733c2f6b65793e0a093c61727261793e0a09093c737472696e673e7465616d69643a555850365945485350573c2f737472696e673e0a09093c737472696e673e6170706c653a3c2f737472696e673e0a093c2f61727261793e0a3c2f646963743e0a3c2f706c6973743e0a";
OSStatus s=SecACLCreateWithSimpleContents(access, nil,
(CFStringRef)newDescription,promptSelector,&newAclEntry);
NSLog(@"%@",SecCopyErrorMessageString(s, NULL));
NSArray *authorizations=@[(id)kSecACLAuthorizationPartitionID];
s= SecACLUpdateAuthorizations(newAclEntry, (CFArrayRef)authorizations);
s=SecACLSetContents(newAclEntry, applicationList, (CFStringRef)newDescription, promptSelector);
As mentioned above, this does create an kSecACLAuthorizationPartitionID ACL entry, but it is ignored.
Avoiding Prompting by Using the System Keychain
Since the login keychain gets a special ACL item (which cannot be updated by a non-Apple process to give eapolclient access without prompting), an alternative method must be performed.
The user keychain was initially selected because other users on the system cannot unlock a login keychain without knowing the login password. Items in the system keychain are unlocked by the system; the system keychain can then be accessed by any admin user on the system.
However, practically speaking, most Macs are single-user systems: those single users have access to both the user and system keychain. If full disk encryption is enabled, the system cannot be started without knowing a user password. SSDs on Macs with the T2 coprocessor and Apple Silicon are encrypted for data at rest protection. For this reason, adding the identity to the system keychain may be a viable option.
The system keychain does not have a kSecACLAuthorizationPartitionID since it is not an iOS-type keychain hybrid, like the login keychain. The identity can be created or imported into the system keychain, ACLs adjusted correctly, and eapolclient configuration setup, resulting in no user prompting.
To successfully configure a client, the following items must be set up:
- X.509 identity in the system keychain, with the correct Access Controls allowing eapolclient access
- Identity preference in the system keychain, associating the X.509 identity with the SSID of the network
- EAPoL profile configuration, specifying the TrustedServerName for the RADIUS Server
- Trust root for the RADIUS server certificate and X.509 certificate
Create or Import Certificate in System Keychain
Using the security command, import a p12 certificate into the system keychain. Our utility, Certificate Request, can create the private key in the system keychain, submit a certificate signing request against an Microsoft Certificate Authority, and then store the signed certificate to the system keychain.
The end result is a valid X.509 identity for authenticating to a 802.1X network.
The eapolclient must be given access to the private key. To give eapolclient access, use this security command:
security import /path/to/p12 -k /Library/Keychains/System.keychain -T "/System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient"
Identity Preference in the System Keychain
The identity preference “com.apple.network.eap.user.identity.wlan.ssid.<SSID NAME>” must be created in the system keychain to associate the SSID with the correct identity. However, using the security command in the system keychain results in an identity preference in the login keychain. We created an open-source tool called “Keychain Detective” to allow for easy creation of an identity preference in the system keychain. You can download it on the Twocanoes Software bitbucket.
To set the identity preference, provide the SSID and subject name or SHA-1 hash of the certificate. For example, to set com.apple.network.eap.system.identity.wlan.ssid identity preference with SSID OneEx and a SHA-1 hash of 8EE806949A3B4FB4B5B30010A4C614E7709D403B in the system keychain:
sudo /Applications/Keychain\ Detective/Contents/MacOS/keychaindetective -i com.apple.network.eap.system.identity.wlan.ssid.OneEx -h 8EE806949A3B4FB4B5B30010A4C614E7709D403B -s
Run
/Applications/Keychain\ Detective/Contents/MacOS/keychaindetective -h
to see all options.
EAPoL Profile Configuration
Download the eapolcfg tool from Twocanoes Software and configure an EAPoL profile configuration. The eapolcfg tool is also included in the Keychain Detective app. For example, with an SSID of OneEx, a profile name of “TCS EAPoL Profile”, and a trusted server name of “web.twocanoes.com”:
./eapolcfg createProfile --SSID OneEx --userDefinedName "TCS EAPoL Profile" --trustedServerName "web.twocanoes.com" --authType 13 --securityType Any
Trusted Root
Using an MDM configuration profile, import the root certificate for your RADIUS server. See your MDM documentation for details.
Testing
Once setup is completed, select the 802.1X network from the AirPort menu item. The Mac should join the network without prompting.
Using Secure Enclave
On modern Macs, keys can be stored in a hardware-backed secure enclave. This prevents private keys from being exported or moved from one Mac to another; it is also the most secure way to store keys. However, the Secure Enclave requires ECC type keys and does not support RSA keys. We have developed CryptoTokenKit extensions that present the X.509 certificate associated with ECC keys. Our extensions also work with system services such as 802.1X, VPN, and more. The same secure enclave identities can be used to authenticate the user (and by extension, the device) for secure networks.
Products and Consulting
Twocanoes Software has a wide range of security solutions, including Certificate Request, Signing Manager, Secure Remote Access, and Smart Card Utility. We are also available for software licensing, security software solution custom development, and consulting. We have developed and integrated solutions for many Fortune 500 companies and US Government agencies. If you are interested, please get in touch.