Script assistance - 802.1x wifi / user keychain

bbot
Contributor

Long story short, a few months ago we changed CA's and found ourselves stuck with updating the existing wifi configuration profile. We ended up having to change the entire company's wifi SSID to get around this. (more info here - https://www.jamf.com/jamf-nation/discussions/23826/config-profile-question)

We ultimately decided to deploy the wifi config profile through a script using the "profiles -I -F" command instead of configuration profile for fear of getting stuck in the same situation. While the cutover to the new CA server went relatively seamless, we don't have a seamless way of updating the wireless certificates. (it only works if the user goes through Self Service)

What I'm looking for:
I'm looking for a way to have seamless wifi certificate renewals.

Here's the problem:
Running the script as the logged on user through Self Service works 100%. It'll remove the old wifi cert, download a new wifi cert, then It'll create the com.apple.network.eap.user.identity.wlan.ssid.SSIDHERE in the user's keychain properly.

When pushing the same exact script from Casper, it'll remove the old wifi cert, download a new wifi ciert, but the com.apple.network.eap.system.identity.wlan.ssid.SSIDHERE keychain entry is created in the system keychain. The user is unable to connect to wifi with this setting.

I've tried multiple variations of things for days and can't get it to work. How are people managing 802.1x wifi profiles and how are you renewing them?

#!/bin/sh

#  Wifi Self Service.sh
#  Script to connect to SSID
#  Brandon Wong 4/24/2017

function connectWifi (){
/usr/bin/profiles -I -F /Library/LC/LC-#.mobileconfig
    Cert="$compname.DOMAIN.com"
    #security set-identity-preference -n -s "com.apple.network.eap.user.identity.wlan.ssid.SSID" /Library/Keychains/System.keychain
    security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID" /Library/Keychains/System.keychain
    #sudo security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID" /Library/Keychains/System.keychain
wservice=`/usr/sbin/networksetup -listallnetworkservices | grep -Ei '(Wi-Fi|AirPort)'`
whwport=`networksetup -listallhardwareports | awk "/$wservice/,/Ethernet Address/" | awk 'NR==2' | cut -d " " -f 2`
hwports=`networksetup -listallhardwareports | awk '/Hardware Port: Wi-Fi/,/Ethernet/' | awk 'NR==2' | cut -d " " -f 2`
wirelessnw=`networksetup -getairportnetwork $hwports | cut -d " " -f 4`
    sleep 10
    if [[ $wirelessnw == "SSID" ]]; then
    echo "Connected to SSID"
    else
    echo "Machine is not connected to SSID. Checking identity preference"
    security get-identity-preference -s "com.apple.network.eap.user.identity.wlan.ssid.SSID" /Library/Keychains/System.keychain
    fi
}

function bindToDomain (){
# Unbind machine from domain
 dsconfigad -remove -force -username macimaging -password $Pass
    sleep 10
    echo "Unbinding"
    killall opendirectoryd
    sleep 5

    ## Testing has shown that unbinding twice may be necessary. 
    dsconfigad -remove -force -username username -password $Pass &> /dev/null
    sleep 10
    echo "Unbinding twice just incase"

    ## Begin rebinding process

    #Basic variables
    computerid=`scutil --get LocalHostName`
    domain=SSID.us
    udn=
    OU="CN=Computers,DC=Corp,DC=#,DC=com"

    #Advanced variables
    alldomains="disable"
    localhome="enable"
    protocol="smb"
    mobile="enable"
    mobileconfirm="disable"
    user_shell="/bin/bash"
    admingroups="Corp"
    namespace="domain"
    packetsign="allow"
    packetencrypt="allow"
    useuncpath="disable"
    passinterval="90"

    # Bind to AD
    dsconfigad -add $domain -alldomains $alldomains -username $udn -password $Pass -computer $computerid -ou "$OU" -force -packetencrypt $packetencrypt
    sleep 1
    echo "Rebinding to AD and setting advanced options"

    #set advanced options
    dsconfigad -localhome $localhome
    sleep 1
    dsconfigad -groups "$admingroups"
    sleep 1
    dsconfigad -mobile $mobile
    sleep 1
    dsconfigad -mobileconfirm $mobileconfirm
    sleep 1
    dsconfigad -alldomains $alldomains
    sleep 1
    dsconfigad -useuncpath "$useuncpath"
    sleep 1
    dsconfigad -protocol $protocol
    sleep 1
    dsconfigad -shell $user_shell
    sleep 1
    dsconfigad -passinterval $passinterval
    sleep 1

    #dsconfigad adds "All Domains"
    # Set the search paths to "custom"
    dscl /Search -create / SearchPolicy CSPSearchPath
    dscl /Search/Contacts -create / SearchPolicy CSPSearchPath

    sleep 1

    # Add the "XXXX.XXXXX.us" search paths
    dscl /Search -append / CSPSearchPath "/Active Directory/CORP/DOMAIN.com"
    dscl /Search/Contacts -append / CSPSearchPath "/Active Directory/CORP/DOMAIN.com"

    sleep 1

    # Delete the "All Domains" search paths
    dscl /Search -delete / CSPSearchPath "/Active Directory/CORP/All Domains"
    dscl /Search/Contacts -delete / CSPSearchPath "/Active Directory/CORP/All Domains"
    dscl /Search -delete / CSPSearchPath "/Active Directory/CORP/#.us"
    dscl /Search/Contacts -delete / CSPSearchPath "/Active Directory/CORP/#.us"
    dscl /Search -delete / CSPSearchPath "/Active Directory/CORP/#.us"
    dscl /Search/Contacts -delete / CSPSearchPath "/Active Directory/CORP/#.us"

    sleep 1

    # Restart opendirectoryd
    killall opendirectoryd
    sleep 3

    dependenciesCheck

}

function dependenciesCheck(){

# Check for an AD computer object
ad_computer_name=`dsconfigad -show | grep "Computer Account" | awk '{print $4}'`
compObj=`dscl /Active Directory/CORP/All Domains read /Computers/$ad_computer_name RecordName | awk '{print $2}'`

if [ $ad_computer_name = $compObj ]; then
    echo Matching AD object exists. Continue script...

    # Make sure mobileconfig exists
    file=/Library/LC/NAMEOFCONFIG.mobileconfig

        if [ -f "$file" ]; then
            connectWifi
                else
                echo Profile missing. Downloading profile
                jamf policy -trigger *TRIGGER*
                    if [ -f "$file" ]; then
                    connectWifi
                        else
                        echo Cannot connect to *SSID*. Mobileconfig is missing.
                        exit 1
        fi
                    fi
else    
    echo "Missing Domain Binding"
    bindToDomain
fi

}

# HARDCODED VALUES ARE SET HERE
Pass=""

# CHECK TO SEE IF VALUES WERE PASSED FOR $4, AND IF SO, ASSIGN THEM
if [ "$4" != "" ] && [ "$Pass" == "" ]; then 
Pass=$4
fi

# Check to make sure Pass variable was passed down from Casper
if [ "$Pass" == "" ]; then 
echo "Error: The parameter 'Pass' is blank. Please specify a value." 
exit 1 
fi

# Variables
currUser=`stat -f "%Su" /dev/console`
echo "Current user is $currUser"

    # Cleanup duplicate machine certificates
    compname=`dsconfigad -show | grep "Computer Account" | awk '{print $4}' | sed 's/.$//'`
    Certs="$compname.#.com"
    hashes=$(security find-certificate -c "$Certs" -a -Z|grep SHA-1|awk '{ print $NF }')
        for hash in $hashes; do
        echo deleting duplicate certs $hash
        sudo security delete-certificate -Z $hash
        done

# Check dependencies 
    dependenciesCheck

exit 0
1 ACCEPTED SOLUTION

mm2270
Legendary Contributor III

@bbot In your script, you keep specifying the System keychain as the target for the security command. /Library/Keychains/System.keychain Is there a reason why? Maybe I'm misunderstanding, but are you attempting to add the wi-fi identity preference into the System keychain, but have user accounts use that identity preference? If so, I don't think that's going to work. As far as I know, identity preferences need to go into the user's keychains. I'm wondering if that is part of the issue here.

As for us, we don't do the cert renewals with a script. We had a custom agent built for us that communicates with our CMS servers. The agent is local (pushed from Jamf Pro) to all Macs and is run by a LaunchAgent every 30 minutes. It checks to see if the Wi-Fi 802.1x certs for the user are valid. If they are it just exits silently. If they are not, the user is prompted with a GUI window to enter their account password, which gets sent back to CMS and will issue a new set of certs to the device.

However, the one thing this agent will not do is create the Wi-Fi identity preference. We do that with a LaunchAgent that keeps checking periodically to make sure if the certs for the user are present, that there is an accompanying identity preference that pairs up the cert to our Wi-Fi SSID.
But it's not a requirement to use a LaunchAgent for that. You can do this with a script called by root, but need to make sure you are running the command to create the identity preference as the logged in user, otherwise it will add the pref to the System keychain, and as you already found out, clients can't use identity prefs in the system keychain.

Try this variation and see if it helps

#!/bin/bash

loggedInUser=$(stat -f%Su /dev/console)
loggedInUID=$(id -u "$loggedInUser")

/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" "/usr/bin/security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID""

View solution in original post

12 REPLIES 12

mm2270
Legendary Contributor III

When a policy pushes a script using the recurring check in trigger, it's running entirely as root, since it's called by a LaunchDaemon. So it makes sense it is adding the wi-fi setting in the System.keychain.

What you have to do is run any commands in the script that must be run as the logged in user, as the logged in user. This question and situation comes up a lot, probably every week here on average, no exaggeration. If you do searches on how to run commands within a script as the logged in user, you should find 100 threads if you find 1. One of them will have a solution for you.
And just to let you know, it's going to be the security set-identity-preference lines that will need to get run as the user, as I'm sure you guessed already.

bbot
Contributor

@mm2270 I was under the impression that running Self Service policies are also executed as root, which left me a bit confused.

I've tried variations of /usr/bin/profiles with the -U and sudo -u /usr/bin/profiles command to specify user to no avail. Been trying to figure this out on and off for the past few months and feel like I'm hitting a dead end.

I've tried making sure to wipe the keychain entry -- security set-identity-preference -n -s "com.apple.network.eap.user.identity.wlan.ssid.SSID " /Library/Keychains/System.keychain

Tried setting it with commands similar to -- security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.LC-Authorized" /Library/Keychains/System.keychain sudo security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.LC-Authorized" /Library/Keychains/System.keychain
sudo -u "$currentUser" -i /usr/bin/security set-identity-preference -s com.apple.network.eap.user.identity.wlan.ssid /Library/Keychains/System.keychain


How are you renewing wifi certificates without user interaction with using /usr/bin/profiles?

bbot
Contributor

To add --
Having the configuration profile set to Computer level generates a computer certificate, just doesn't assign the identity properly.
Having the configuration profile set to User level doesn't generate a cert at all.

mm2270
Legendary Contributor III

@bbot In your script, you keep specifying the System keychain as the target for the security command. /Library/Keychains/System.keychain Is there a reason why? Maybe I'm misunderstanding, but are you attempting to add the wi-fi identity preference into the System keychain, but have user accounts use that identity preference? If so, I don't think that's going to work. As far as I know, identity preferences need to go into the user's keychains. I'm wondering if that is part of the issue here.

As for us, we don't do the cert renewals with a script. We had a custom agent built for us that communicates with our CMS servers. The agent is local (pushed from Jamf Pro) to all Macs and is run by a LaunchAgent every 30 minutes. It checks to see if the Wi-Fi 802.1x certs for the user are valid. If they are it just exits silently. If they are not, the user is prompted with a GUI window to enter their account password, which gets sent back to CMS and will issue a new set of certs to the device.

However, the one thing this agent will not do is create the Wi-Fi identity preference. We do that with a LaunchAgent that keeps checking periodically to make sure if the certs for the user are present, that there is an accompanying identity preference that pairs up the cert to our Wi-Fi SSID.
But it's not a requirement to use a LaunchAgent for that. You can do this with a script called by root, but need to make sure you are running the command to create the identity preference as the logged in user, otherwise it will add the pref to the System keychain, and as you already found out, clients can't use identity prefs in the system keychain.

Try this variation and see if it helps

#!/bin/bash

loggedInUser=$(stat -f%Su /dev/console)
loggedInUID=$(id -u "$loggedInUser")

/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" "/usr/bin/security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID""

bbot
Contributor

@mm2270 I have to double check, but I believe why I had to specify /Library/Keychains/System.keychain was because the machine certificate that is requested is placed in the System keychain.

I am trying to have the identity preference in the login keychain, not the system.

Generating the certificate isn't the issue for us, the mobileconfig does that perfectly. It sounds like what you're suggesting to create the identity pref in the user keychain will work...

I've tried /usr/bin/security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID as my current logged in user and confirmed this creates the identity preference in the login keychain; however... when I try "sudo -iu "$loggedInUser" "/usr/bin/security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID""" -- nothing happens.

I'm wondering if there's some security in-place that prevents the creation of keychain items in other user's keychain.

Thanks for your time on this. I really appreciate it.

bbot
Contributor

Wait, I'm on to something. I wasn't using the UID for "launchctl asuser" Looks like I was able to create the identity in the login keychain when specifying the UID. (i overlooked it and put the user account) Going to test and will post back here.

bbot
Contributor

@mm2270 It works! The trick was to use launchctl to run it as the user. I owe you big time.

Now I just need to create an EA that can help me determine whether a certificate needs renewing or not, then I can use the profiles command to generate a new certificate from my config profile...then use launchctl and /usr/binsecurity to create the identity preference.

Thanks so much!

On a side note, using the same format you provided didn't work for some reason. /bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" "/usr/bin/security set-identity-preference -c "$Cert" -s "com.apple.network.eap.user.identity.wlan.ssid.SSID""

I took out the " and the and it works. (it was giving me an error about the file or directory doesn't exist)

stutz
Contributor

This also helped me as well.

One thing I did notice is that WiFi doesn't work at the login screen. When you logged back in it auto connects just fine.

Anyone got a solution to keep the WiFi connection at the login screen?

dlondon
Valued Contributor

This looks like something I need too so I tried the script @mm2270 wrote above but like @bbot I got the error "the file or directory doesn't exist". I couldn't see a fixed version so started playing with the script and got it to this state where it works (probably more by luck)

#!/bin/bash

loggedInUser=$(stat -f%Su /dev/console)
loggedInUID=$(id -u "$loggedInUser")
Cert=$(hostname)

/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" /usr/bin/security set-identity-preference -c "$Cert" -s com.apple.network.eap.user.identity.wlan.ssid.Unifi /Library/Keychains/System.keychain

For us the Certificate is a machine certificate with the name being the hostname and "Unifi" is the SSID of our Wifi

It does set the preference up but then I had a few other problems that I'm hoping someone might have some suggestions for.

The first thing I found was that the wireless "Unifi" is not set up as the default but am hoping there is some way to to fix
a21dd59bdf32442eabcef58e48ae68cf

But I can at worst manually choose the correct one
2fcd7c29a39f406898fb47ef8c9dc313

Then the user is asked to allow eapolclient to access the logon keychain 1e5caf87b5ea406cb9d2a4cb62bd0f80

This is the one I'm most worried about because it's asking for access to the system keychain. For the life of me I can't see a way to do that in the command line and I'm not sure what is wanting that access.

06e4bbec80114094a004044a0c041d1e

The last issue is that when all this is set up I get this and I have to disconnect and then reconnect via the button before it actually

359e99fd58794f5393f1bc337f689f39

mwilkerson
New Contributor III

@dlondon Any update on this? We're now testing the same thing and the admin password for access to the System keychain is my biggest obstacle at this point.

dlondon
Valued Contributor

Sorry @mwilkerson no success with the admin prompt for System keychain

mwilkerson
New Contributor III

@dlondon I discovered yesterday that if I dismiss the admin credentials prompt (hit esc or click Deny), it joins the wifi network anyway... So, even though it comes up, it doesn't seem like it's even necessary. Now if I can just get it to not prompt at all....