802.1x in Lion for Macbook Air needing AD domain authentication

Not applicable

How do I configure the 802.1x profile so our MBAirs can login with AD
domain accounts within Lion. I have seen a bunch of posts explaining
how to use the iPhone Config Utility, but I'm not seeing an area to
configure the 802.1x settings, just the wireless connections. Is there
anything easier or managed through JSS that could resolve this issue.

Charlette

Modesto City Schools

20 REPLIES 20

daniel_behan
Contributor III

Charlette,

Creating a Wireless Profile will create an 802.1x profile. For my workaround, I used Lion Server to create a profile that contained the certificates for my network and my wireless configuration. I exported the configuration and made a .pkg file of the profile in /private/tmp with a post flight script that simply installed the profile by means of the profiles command ex (profiles -I -F {path to profile})

Snickasaurus
Contributor

Daniel could you please post your script or relevant part.

jhbush
Valued Contributor II

I use this as post flight script to install the profile ```

postflight

/usr/bin/profiles -I -F /tmp/myprofile.mobileconfig
```

I generated my profile from iPCU and did the final edits in text edit to add the login window profile settings.

This is the snip it from my xml file for my mobile config. The important part for 802.1x is the setup mode portion the rest is all generated from iPCU. This will use kerberos to request the ticket and get you on to the network.

<?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>PayloadContent</key> <array> <dict> <key>AutoJoin</key> <false/> <key>EAPClientConfiguration</key> <dict> <key>AcceptEAPTypes</key> <array> <integer>25</integer> </array> <key>EAPFASTProvisionPAC</key> <false/> <key>EAPFASTProvisionPACAnonymously</key> <false/> <key>EAPFASTUsePAC</key> <false/> <key>PayloadCertificateAnchorUUID</key> <array> <string>000000</string> <string>000000</string> </array> <key>TLSAllowTrustExceptions</key> <true/> <key>TLSTrustedServerNames</key> <array> <string>0.0.0.0</string> <string>0.0.0.0</string> </array> </dict> <key>EncryptionType</key> <string>WPA</string> <key>HIDDEN_NETWORK</key> <true/> <key>PayloadDescription</key> <string>Configures wireless connectivity settings.</string> <key>PayloadDisplayName</key> <string>Wi-Fi (Hidden-Network)</string> <key>PayloadIdentifier</key> <string>com.expedia.profile.wifi</string> <key>PayloadOrganization</key> <string>YourCompany</string> <key>PayloadType</key> <string>com.apple.wifi.managed</string> <key>PayloadUUID</key> <string>00000000000</string> <key>PayloadVersion</key> <integer>1</integer> <key>ProxyType</key> <string>None</string> <key>SSID_STR</key> <string>YourNetwork</string> <key>SetupModes</key> <array> <string>Loginwindow</string> </array> </dict> <dict>

nkalister
Valued Contributor

if you want to get REALLY crazy, I've got a script I've written that creates a certificate signing request and submits it to an Active Directory certificate authority. Then it downloads the signed certificate, combines it with the private key into a pkcs12 file, inserts that pkcs12 file into a mobileconfig file with Wifi and wired ethernet system-level 802.1x support, and then installs the mobileconfig. Finally, the script writes a couple of log files that can be used to report on the user's certificate expiration date . . I've got an extension attribute that parses the log files and shows me on my JSS how many days are left until expiration on each client machine's cert.
And, unlike apple's ADcertificatepayloadplugin, my script will work if your web front end and certificate authority are not living on the same server.
If anyone's interested, I can post the script and extension attribute . . .

Snickasaurus
Contributor

nkalister I am VERY interested in seeing this script!! This might be just what I need. I have the .mobileconfig I made last night though it hasn't been tested yet with our test group. My main problem, being new to the Mac world, is finding a way for users to be authenticated to the wireless AP without ever having to enter credentials because they have already been authenticated through AD when logging into the machine. So basically log in once and have access to everything you are allowed simply because you have to authenticate through AD to even load your profile on the client machine.

nkalister
Valued Contributor

well, snick, the environment that I'm in uses certificates to authenticate wifi, no usernames and passwords needed. If you are using usernames and passwords to authenticate to wifi, then my script wouldn't help you, unfortunately . . .

Snickasaurus
Contributor

Doesn't really matter. I love scripting and any I get a chance to look at and pull apart to see how it works will further my scripting knowledge. And that is exciting!

stevewood
Honored Contributor II
Honored Contributor II

Hey Nick, send it along anyway. We are using names/passwords, but changing over to certs might be a better move for us here. I'm tired of people not being able to connect to wi-fi because they changed their password.

nkalister
Valued Contributor

ok guys, I'll post in the next day or so. I have to do some cleanup on it first- there's a few things specific to our environment I need to genericize . . .

tlarkin
Honored Contributor

Oh man you guys got Nick to go crazy....

It looks like you have got a lot done since I was out there last Nick. Glad to see you got a solution working. I know that was one of your concerns during the jump start.

I am pretty excited to see what you created, and thanks for sharing!

-Tom

nkalister
Valued Contributor

LOL, hey tom!! yeah, we've got everything working well, except for using kerberos to authenticate to the CA. that looks to be an IIS issue on our CA, though, so I'm working on that with Microsoft. Once we get kerberos authentication to the CA working, then we can do silent renewals and we'll have complete parity with windows client machines.
Anyway, it looks like I may not be able to post the script until monday- I just discovered that winmagic encryption breaks Adobe installers, so I've got to work on that first!

nkalister
Valued Contributor

OK, this is a big script, so I'm going to post pieces with comments and then the whole thing at the end.
Also, I'm pretty new to scripting, so please let me know if i'm making any obvious bone-head errors- though this is tested and working reliably for me I'd like to improve error handling and logging.
So, first on the agenda is configuring some temp file locations, the location of the CA web enrollment server, the certificate template, and a few other items that are self-evident:

#!/bin/bash

# The URL of the CA web enrollment page.
# Would be great to find a way to programmatically find this out...
# generally will be https://hostname/certsrv

CA_URL="insert your CA web enrollment url here"

# Certificate template name in preparation for curling the CSR to the CA
CERT_TYPE_USER="insert your template name here"

## Default username and password
USER_NAME=
USER_PASSWORD=

## Set temp directory and intermediary file names and locations.
TEMP_DIR=~/Desktop/.csr_workspace
KEY="${TEMP_DIR}/autoenroll.key"
CSR="${TEMP_DIR}/autoenroll.csr"
DER="${TEMP_DIR}/certnew.cer"
PEM="${TEMP_DIR}/certnew.pem"
PK12="${TEMP_DIR}/autoenroll.p12"
MOBILECONFIG="${TEMP_DIR}/autoenroll.mobileconfig"

## Set PlistBuddy location
PLISTBUDDY="/usr/libexec/PlistBuddy"

## Find out if this machine needs Wi-Fi set up
HAS_WIFI=`networksetup -listallhardwareports | grep Wi-Fi`

## Get any supplied options.

while getopts p:u: SWITCH
do
    case $SWITCH in
        p) USER_PASSWORD=$OPTARG;;
        u) USER_NAME=$OPTARG;;
    esac
done

With that out of the way, we start in on the first function, which prompts the user for their username:

# Functions

## Get the username and set it up to match their kerberos principal
get_username() {
    read -p "Please enter the domain username for this certificate: " USER_NAME
    KERB_USER_NAME=$USER_NAME@corp.hds.com
    if [ "$USER_NAME" = "" ]; then
        echo "You did not enter a username, so I'll just exit."
        exit 20
    fi
}

Then, we get the user's password:

## Get the user's domain password
get_password() {
    stty_orig=`stty -g`
    stty -echo
    read -p "Please enter your domain password: " USER_PASSWORD
    stty $stty_orig
    echo ""
    if [ "$USER_PASSWORD" = "" ]; then
        echo "You did not enter a password, so I'll just exit."
        exit 21
    fi
}

Next, the machine name:

## Get the machine name
get_machine_name() {
    MACHINE_NAME=`scutil --get ComputerName`
}

At this point, we have everything we need to submit a certificate signing request to our CA, so the next step is to create a CSR using openssl and then save the CSR and private key to our temp location:

## Generate csr with openssl
generate_csr_user() {
    /usr/bin/openssl req -new -batch -newkey rsa:2048 -nodes -outform PEM -keyout "${KEY}" -out "${CSR}" -subj "/CN=${KERB_USER_NAME}"
}

Now we need to upload the CSR to the CA's web enrollment page. First the CSR needs to be URL-encoded, then it's submitted using curl. Right now, the curl command is authenticating to the CA using NTLM. I'd prefer to use GSS Negotiate, but that is not working in our environment at the moment. After uploading the CSR, we check to make sure that the CA issued us a request ID. If the submission failed for any reason, we will not receive an ID, so I check for that and exit if it is not returned.
This is messy, I'll admit that straight up- the sed operation came from an old script I got from apple, and I haven't bothered to try to figure out why it isn't fully isolating the request ID.

## curl the csr up
curl_csr() {
# First we url-encode the csr
ENCODED_CSR=`cat ${CSR} | hexdump -v -e '1/1 "%02x	"' -e '1/1 "%_c
"' |
LANG=C awk '
    $1 == "20"                      { printf("%s",      "+");   next    }
    $2 ~  /^[a-zA-Z0-9.*()/-]$/    { printf("%s",      $2);    next    }
                                    { printf("%%%s",    $1)             }'`


    # Now to post this to the Web Enrollment page.
    # We'll need to capture the ReqID when it finishes. 

    REQ_ID=`curl -k --ntlm -u ${USER_NAME}:${USER_PASSWORD} -d CertRequest=${ENCODED_CSR} -d SaveCert=yes -d Mode=newreq -d CertAttrib=CertificateTemplate:"${CERT_TYPE_USER}" ${CA_URL}/certfnsh.asp | grep ReqID | sed -e 's/.*ReqID=(.*)&amp.*/1/g'`
    REQ_ID=`echo $REQ_ID | cut -d " " -f 1`
    echo REQ_ID is ${REQ_ID}

    # Verify if we actually have a ReqID - if not, we need to bail.
    if [ ! "$REQ_ID" ]; then 
        echo "WARNING: I didn't receive a request ID from the Certificate Authority."
        echo "This likely means the certificate request failed - please contact your administrator."
        exit 22
    fi

}

Now that we've submitted the CSR, we need to download our signed certificate, using curl again. The certificate is saved as a PEM file to our temporary location.

## curl down the cert
curl_cert() {
    #NOTE:  curl accepts whatever cert the server is using, to make it more likely to work in different
    #       environments. Also this pulls the cert down in PEM format, which needs to be converted into a
    #       PKCS12 file and then imported into the keychain.
    ##
    echo CA_USR is ${CA_URL}
    curl -k -o ${PEM} -A "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5" --ntlm -u ${USER_NAME}:${USER_PASSWORD} "${CA_URL}/certnew.cer?ReqID=${REQ_ID}&Enc=b64"
}

Now that we've got a signed cert and a private key, we need to combine those into a single PKCS12 file that can be inserted into our mobileconfig file template. This is done using openssl again:

## Build the cert and private key into a PKCS12
pack() {
    openssl pkcs12 -export -in ${PEM} -inkey ${KEY} -out ${PK12} -name "$MACHINE_NAME" -passout pass:pass
}

Now we're ready to create a mobileconfig file containing our shiny new pkcs12 identity. I have 2 templates in the script- one contains wi-fi settings, the other configures wired ethernet only since the profile will refuse to install if a wi-fi payload is present, and there is no wi-fi hardware on the client. You will need to create your own templates using Profile Manager- there are just too many environment-specific settings in the XML, but I'm including them here so you can see how I'm creating the file and then using plistbuddy to insert the pkcs12 file.
This is the Ethernet only mobileconfig creation function:

## Build a mobileconfig profile around the PKCS12 file
build_mobileconfig() {
echo " <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PayloadContent</key>
    <array>
        <dict>
            <key>Password</key>
            <string>pass</string>
            <key>PayloadContent</key>
            <data>
            </data>
            <key>PayloadDisplayName</key>
            <string>My Certificate</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.certificate.45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadType</key>
            <string>com.apple.security.pkcs12</string>
            <key>PayloadUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>AuthenticationMethod</key>
            <string></string>
            <key>AutoJoin</key>
            <true/>
            <key>EAPClientConfiguration</key>
            <dict>
                <key>AcceptEAPTypes</key>
                <array>
                    <integer>13</integer>
                </array>
                <key>TTLSInnerAuthentication</key>
                <string>MSCHAPv2</string>
            </dict>
            <key>EncryptionType</key>
            <string>Any</string>
            <key>HIDDEN_NETWORK</key>
            <false/>
            <key>Interface</key>
            <string>FirstActiveEthernet</string>
            <key>PayloadCertificateUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadDisplayName</key>
            <string>Wired 802.1X</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.interfaces.c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadType</key>
            <string>com.apple.firstactiveethernet.managed</string>
            <key>PayloadUUID</key>
            <string>c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>ProxyType</key>
            <string>None</string>
            <key>SetupModes</key>
            <array>
                <string>System</string>
            </array>
        </dict>
    </array>
    <key>PayloadDisplayName</key>
    <string>HDS 802.1x Settings</string>
    <key>PayloadIdentifier</key>
    <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte</string>
    <key>PayloadOrganization</key>
    <string>HDS</string>
    <key>PayloadRemovalDisallowed</key>
    <false/>
    <key>PayloadScope</key>
    <string>System</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadUUID</key>
    <string>ed262b70-2d97-012f-81de-001c42cd16e3</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
</dict>
</plist>" > ${MOBILECONFIG}
    ## Use plistbuddy to insert the pkcs12 file into the mobileconfig
    $PLISTBUDDY -c "Import PayloadContent:0:PayloadContent ${PK12}" ${MOBILECONFIG}
}

And the mobileconfig with Wi-Fi as well:

## Build a mobileconfig profile with Wi-Fi settings included around the PKCS12 file
build_wifi_mobileconfig() {
echo " <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PayloadContent</key>
    <array>
        <dict>
            <key>Password</key>
            <string>pass</string>
            <key>PayloadContent</key>
            <data>
            </data>
            <key>PayloadDisplayName</key>
            <string>My Certificate</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.certificate.45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadType</key>
            <string>com.apple.security.pkcs12</string>
            <key>PayloadUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>AuthenticationMethod</key>
            <string></string>
            <key>AutoJoin</key>
            <false/>
            <key>EAPClientConfiguration</key>
            <dict>
                <key>AcceptEAPTypes</key>
                <array>
                    <integer>13</integer>
                </array>
                <key>TTLSInnerAuthentication</key>
                <string>MSCHAPv2</string>
            </dict>
            <key>EncryptionType</key>
            <string>WPA</string>
            <key>HIDDEN_NETWORK</key>
            <false/>
            <key>Interface</key>
            <string>BuiltInWireless</string>
            <key>PayloadCertificateUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadDisplayName</key>
            <string>WiFi (HDSCorp)</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.interfaces.5f557f60-2d98-012f-81e0-001c42cd16e3</string>
            <key>PayloadType</key>
            <string>com.apple.wifi.managed</string>
            <key>PayloadUUID</key>
            <string>5f557f60-2d98-012f-81e0-001c42cd16e3</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>ProxyType</key>
            <string>None</string>
            <key>SSID_STR</key>
            <string>HDSCorp</string>
            <key>SetupModes</key>
            <array>
                <string>System</string>
            </array>
        </dict>
        <dict>
            <key>AuthenticationMethod</key>
            <string></string>
            <key>AutoJoin</key>
            <true/>
            <key>EAPClientConfiguration</key>
            <dict>
                <key>AcceptEAPTypes</key>
                <array>
                    <integer>13</integer>
                </array>
                <key>TTLSInnerAuthentication</key>
                <string>MSCHAPv2</string>
            </dict>
            <key>EncryptionType</key>
            <string>Any</string>
            <key>HIDDEN_NETWORK</key>
            <false/>
            <key>Interface</key>
            <string>FirstActiveEthernet</string>
            <key>PayloadCertificateUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadDisplayName</key>
            <string>Wired 802.1X</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.interfaces.c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadType</key>
            <string>com.apple.firstactiveethernet.managed</string>
            <key>PayloadUUID</key>
            <string>c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>ProxyType</key>
            <string>None</string>
            <key>SetupModes</key>
            <array>
                <string>System</string>
            </array>
        </dict>
    </array>
    <key>PayloadDisplayName</key>
    <string>HDS 802.1x Settings</string>
    <key>PayloadIdentifier</key>
    <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte</string>
    <key>PayloadOrganization</key>
    <string>HDS</string>
    <key>PayloadRemovalDisallowed</key>
    <false/>
    <key>PayloadScope</key>
    <string>System</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadUUID</key>
    <string>ed262b70-2d97-012f-81de-001c42cd16e3</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
</dict>
</plist>" > ${MOBILECONFIG}
    ## Use plistbuddy to insert the pkcs12 file into the mobileconfig
    $PLISTBUDDY -c "Import PayloadContent:0:PayloadContent ${PK12}" ${MOBILECONFIG}
}

Now that we have our mobileconfig file complete, we can install it using the profiles command:

## Install the mobileconfig profile
install_mobileconfig() {
    profiles -I -F ${MOBILECONFIG}
}

Finally, I create some log files with date stamps to report on the certificate expiration and issue dates. I would guess that there is a way to read this information direct from the certificates themselves, but I haven't looked into that yet.
Also, you'll obviously want to change the location that the files are saved to . . . I only need to accommodate one user per machine, so I'm not worrying about reporting on multiple certs on the same machine.

## Write out installation and expiration times so we can notify the user later before their cert expires
setup_notification() {
    now_seconds=`date +%s`
    # Make sure the HDS library location exists and create it if not
    if [ -d "/Library/HDS" ]; then
        echo "HDS Library directory found"
    else
        echo "Creating the HDS library directory"
        mkdir -m 755 /Library/HDS
    fi
    # Create a file to hold the date value
    touch /Library/HDS/cert_installation_time
    # Insert the value into the file
    echo "$now_seconds" > /Library/HDS/cert_installation_time
    ## Add a years worth of seconds to our current date to get the certificate's expiration date, leap years can suck it
    expire_seconds=$(($now_seconds + 31556926))
    # and then write that out to another file
    touch /Library/HDS/cert_expiration_time
    echo "$expire_seconds" > /Library/HDS/cert_expiration_time

And that's all the functions, folks. All that's left is the actual script, which makes sure this was run as root, calls our functions, decides which mobileconfig template will be used, and cleans up the temp location:

## Actual script here:
# check to see if this was run as sudo, and exit if not
if [ "$(whoami)" != "root" ]; then
    echo "You need to run this script as root, please use sudo"
    exit 1
else
    mkdir -m 777 $TEMP_DIR
    get_username
    get_password
    get_machine_name
    generate_csr_user
    curl_csr
    curl_cert
    pack
    if [ "$HAS_WIFI" == "" ]; then
        echo "no Wi-Fi found"
        build_mobileconfig
    else
        echo "Wi-Fi found, we will configure it"
        build_wifi_mobileconfig
    fi
    install_mobileconfig
    setup_notification
    #delete temp dir and files
    rm -r $TEMP_DIR/
fi

Hopefully that all makes some kind of sense.
Here's the full script:

#!/bin/bash

# The URL of the CA web enrollment page.
# Would be great to find a way to programmatically find this out...
# generally will be https://hostname/certsrv

CA_URL="insert your CA web enrollment url here"

# Certificate template name in preparation for curling the CSR to the CA
CERT_TYPE_USER="insert your template name here"

## Default username and password
USER_NAME=
USER_PASSWORD=

## Set temp directory and intermediary file names and locations.
TEMP_DIR=~/Desktop/.csr_workspace
KEY="${TEMP_DIR}/autoenroll.key"
CSR="${TEMP_DIR}/autoenroll.csr"
DER="${TEMP_DIR}/certnew.cer"
PEM="${TEMP_DIR}/certnew.pem"
PK12="${TEMP_DIR}/autoenroll.p12"
MOBILECONFIG="${TEMP_DIR}/autoenroll.mobileconfig"

## Set PlistBuddy location
PLISTBUDDY="/usr/libexec/PlistBuddy"

## Find out if this machine needs Wi-Fi set up
HAS_WIFI=`networksetup -listallhardwareports | grep Wi-Fi`

## Get any supplied options.

while getopts p:u: SWITCH
do
    case $SWITCH in
        p) USER_PASSWORD=$OPTARG;;
        u) USER_NAME=$OPTARG;;
    esac
done

# Functions

## Get the username and set it up to match their kerberos principal
get_username() {
    read -p "Please enter the domain username for this certificate: " USER_NAME
    KERB_USER_NAME=$USER_NAME@corp.hds.com
    if [ "$USER_NAME" = "" ]; then
        echo "You did not enter a username, so I'll just exit."
        exit 20
    fi
}
## Get the user's domain password
get_password() {
    stty_orig=`stty -g`
    stty -echo
    read -p "Please enter your domain password: " USER_PASSWORD
    stty $stty_orig
    echo ""
    if [ "$USER_PASSWORD" = "" ]; then
        echo "You did not enter a password, so I'll just exit."
        exit 21
    fi
}

## Get the machine name
get_machine_name() {
    MACHINE_NAME=`scutil --get ComputerName`
}

## Generate csr with openssl
generate_csr_user() {
    /usr/bin/openssl req -new -batch -newkey rsa:2048 -nodes -outform PEM -keyout "${KEY}" -out "${CSR}" -subj "/CN=${KERB_USER_NAME}"
}

## curl the csr up
curl_csr() {
# First we url-encode the csr
ENCODED_CSR=`cat ${CSR} | hexdump -v -e '1/1 "%02x	"' -e '1/1 "%_c
"' |
LANG=C awk '
    $1 == "20"                      { printf("%s",      "+");   next    }
    $2 ~  /^[a-zA-Z0-9.*()/-]$/    { printf("%s",      $2);    next    }
                                    { printf("%%%s",    $1)             }'`


    # Now to post this to the Web Enrollment page.
    # We'll need to capture the ReqID when it finishes. 

    REQ_ID=`curl -k --ntlm -u ${USER_NAME}:${USER_PASSWORD} -d CertRequest=${ENCODED_CSR} -d SaveCert=yes -d Mode=newreq -d CertAttrib=CertificateTemplate:"${CERT_TYPE_USER}" ${CA_URL}/certfnsh.asp | grep ReqID | sed -e 's/.*ReqID=(.*)&amp.*/1/g'`
    REQ_ID=`echo $REQ_ID | cut -d " " -f 1`
    echo REQ_ID is ${REQ_ID}

    # Verify if we actually have a ReqID - if not, we need to bail.
    if [ ! "$REQ_ID" ]; then 
        echo "WARNING: I didn't receive a request ID from the Certificate Authority."
        echo "This likely means the certificate request failed - please contact your administrator."
        exit 22
    fi

}

## curl down the cert
curl_cert() {
    #NOTE:  curl accepts whatever cert the server is using, to make it more likely to work in different
    #       environments. Also this pulls the cert down in PEM format, which needs to be converted into a
    #       PKCS12 file and then imported into the keychain.
    ##
    echo CA_USR is ${CA_URL}
    curl -k -o ${PEM} -A "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5" --ntlm -u ${USER_NAME}:${USER_PASSWORD} "${CA_URL}/certnew.cer?ReqID=${REQ_ID}&Enc=b64"
}

## Build the cert and private key into a PKCS12
pack() {
    openssl pkcs12 -export -in ${PEM} -inkey ${KEY} -out ${PK12} -name "$MACHINE_NAME" -passout pass:pass
}

## Build a mobileconfig profile around the PKCS12 file
build_mobileconfig() {
echo " <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PayloadContent</key>
    <array>
        <dict>
            <key>Password</key>
            <string>pass</string>
            <key>PayloadContent</key>
            <data>
            </data>
            <key>PayloadDisplayName</key>
            <string>My Certificate</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.certificate.45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadType</key>
            <string>com.apple.security.pkcs12</string>
            <key>PayloadUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>AuthenticationMethod</key>
            <string></string>
            <key>AutoJoin</key>
            <true/>
            <key>EAPClientConfiguration</key>
            <dict>
                <key>AcceptEAPTypes</key>
                <array>
                    <integer>13</integer>
                </array>
                <key>TTLSInnerAuthentication</key>
                <string>MSCHAPv2</string>
            </dict>
            <key>EncryptionType</key>
            <string>Any</string>
            <key>HIDDEN_NETWORK</key>
            <false/>
            <key>Interface</key>
            <string>FirstActiveEthernet</string>
            <key>PayloadCertificateUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadDisplayName</key>
            <string>Wired 802.1X</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.interfaces.c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadType</key>
            <string>com.apple.firstactiveethernet.managed</string>
            <key>PayloadUUID</key>
            <string>c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>ProxyType</key>
            <string>None</string>
            <key>SetupModes</key>
            <array>
                <string>System</string>
            </array>
        </dict>
    </array>
    <key>PayloadDisplayName</key>
    <string>HDS 802.1x Settings</string>
    <key>PayloadIdentifier</key>
    <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte</string>
    <key>PayloadOrganization</key>
    <string>HDS</string>
    <key>PayloadRemovalDisallowed</key>
    <false/>
    <key>PayloadScope</key>
    <string>System</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadUUID</key>
    <string>ed262b70-2d97-012f-81de-001c42cd16e3</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
</dict>
</plist>" > ${MOBILECONFIG}
    ## Use plistbuddy to insert the pkcs12 file into the mobileconfig
    $PLISTBUDDY -c "Import PayloadContent:0:PayloadContent ${PK12}" ${MOBILECONFIG}
}

## Build a mobileconfig profile with Wi-Fi settings included around the PKCS12 file
build_wifi_mobileconfig() {
echo " <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PayloadContent</key>
    <array>
        <dict>
            <key>Password</key>
            <string>pass</string>
            <key>PayloadContent</key>
            <data>
            </data>
            <key>PayloadDisplayName</key>
            <string>My Certificate</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.certificate.45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadType</key>
            <string>com.apple.security.pkcs12</string>
            <key>PayloadUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>AuthenticationMethod</key>
            <string></string>
            <key>AutoJoin</key>
            <false/>
            <key>EAPClientConfiguration</key>
            <dict>
                <key>AcceptEAPTypes</key>
                <array>
                    <integer>13</integer>
                </array>
                <key>TTLSInnerAuthentication</key>
                <string>MSCHAPv2</string>
            </dict>
            <key>EncryptionType</key>
            <string>WPA</string>
            <key>HIDDEN_NETWORK</key>
            <false/>
            <key>Interface</key>
            <string>BuiltInWireless</string>
            <key>PayloadCertificateUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadDisplayName</key>
            <string>WiFi (HDSCorp)</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.interfaces.5f557f60-2d98-012f-81e0-001c42cd16e3</string>
            <key>PayloadType</key>
            <string>com.apple.wifi.managed</string>
            <key>PayloadUUID</key>
            <string>5f557f60-2d98-012f-81e0-001c42cd16e3</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>ProxyType</key>
            <string>None</string>
            <key>SSID_STR</key>
            <string>HDSCorp</string>
            <key>SetupModes</key>
            <array>
                <string>System</string>
            </array>
        </dict>
        <dict>
            <key>AuthenticationMethod</key>
            <string></string>
            <key>AutoJoin</key>
            <true/>
            <key>EAPClientConfiguration</key>
            <dict>
                <key>AcceptEAPTypes</key>
                <array>
                    <integer>13</integer>
                </array>
                <key>TTLSInnerAuthentication</key>
                <string>MSCHAPv2</string>
            </dict>
            <key>EncryptionType</key>
            <string>Any</string>
            <key>HIDDEN_NETWORK</key>
            <false/>
            <key>Interface</key>
            <string>FirstActiveEthernet</string>
            <key>PayloadCertificateUUID</key>
            <string>45F8D5A7-F8EA-4C7A-9443-C491E5F4605E</string>
            <key>PayloadDisplayName</key>
            <string>Wired 802.1X</string>
            <key>PayloadEnabled</key>
            <true/>
            <key>PayloadIdentifier</key>
            <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte.interfaces.c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadType</key>
            <string>com.apple.firstactiveethernet.managed</string>
            <key>PayloadUUID</key>
            <string>c88c5970-2d98-012f-81e1-001c42cd16e3</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>ProxyType</key>
            <string>None</string>
            <key>SetupModes</key>
            <array>
                <string>System</string>
            </array>
        </dict>
    </array>
    <key>PayloadDisplayName</key>
    <string>HDS 802.1x Settings</string>
    <key>PayloadIdentifier</key>
    <string>com.apple.mdm.virtualion-server.local.ed262b70-2d97-012f-81de-001c42cd16e3.alacarte</string>
    <key>PayloadOrganization</key>
    <string>HDS</string>
    <key>PayloadRemovalDisallowed</key>
    <false/>
    <key>PayloadScope</key>
    <string>System</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadUUID</key>
    <string>ed262b70-2d97-012f-81de-001c42cd16e3</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
</dict>
</plist>" > ${MOBILECONFIG}
    ## Use plistbuddy to insert the pkcs12 file into the mobileconfig
    $PLISTBUDDY -c "Import PayloadContent:0:PayloadContent ${PK12}" ${MOBILECONFIG}
}

## Install the mobileconfig profile
install_mobileconfig() {
    profiles -I -F ${MOBILECONFIG}
}

## Write out installation and expiration times so we can notify the user later before their cert expires
setup_notification() {
    now_seconds=`date +%s`
    # Make sure the HDS library location exists and create it if not
    if [ -d "/Library/HDS" ]; then
        echo "HDS Library directory found"
    else
        echo "Creating the HDS library directory"
        mkdir -m 755 /Library/HDS
    fi
    # Create a file to hold the date value
    touch /Library/HDS/cert_installation_time
    # Insert the value into the file
    echo "$now_seconds" > /Library/HDS/cert_installation_time
    ## Add a years worth of seconds to our current date to get the certificate's expiration date, leap years can suck it
    expire_seconds=$(($now_seconds + 31556926))
    # and then write that out to another file
    touch /Library/HDS/cert_expiration_time
    echo "$expire_seconds" > /Library/HDS/cert_expiration_time
}

## Actual script here:
# check to see if this was run as sudo, and exit if not
if [ "$(whoami)" != "root" ]; then
    echo "You need to run this script as root, please use sudo"
    exit 1
else
    mkdir -m 777 $TEMP_DIR
    get_username
    get_password
    get_machine_name
    generate_csr_user
    curl_csr
    curl_cert
    pack
    if [ "$HAS_WIFI" == "" ]; then
        echo "no Wi-Fi found"
        build_mobileconfig
    else
        echo "Wi-Fi found, we will configure it"
        build_wifi_mobileconfig
    fi
    install_mobileconfig
    setup_notification
    #delete temp dir and files
    rm -r $TEMP_DIR/
fi

nkalister
Valued Contributor

And here's the extension attribute I'm using to get the expiration date into the JSS. This is set up as a 'populate from script' ea, with the data type set to integer:

#!/bin/sh

# Check to see if we have a certificate expiration time
if [ -f "/Library/HDS/cert_expiration_time" ]; then
    now=`date +%s`
    expire_seconds=`cat /Library/HDS/cert_expiration_time`
    datediff=$((expire_seconds - now))
    days_to_expire=$(($datediff / 86400))
    echo "<result>$days_to_expire</result>"
else
    echo "<result>No certificate installed</result>"
fi

jvv
New Contributor

nkalister, could you please help me on this error:

sudo profiles -I -F /Users/User/Desktop/csr/autoenroll.mobileconfig
read_regf_block: invalid checksum
regfio_open: Failed to read initial REGF block
Failed to open /Users/jivkovelikov/Desktop/csr/autoenroll.mobileconfig!
Error was (No such file or directory)

autoenroll.mobileconfig is actually in place.

Please help me. I realy need this script working. The system is 10.6.8 if it matters
Thank you!

nkalister
Valued Contributor

sorry jvv- mobileconfig profiles are only supported in 10.7!
There is good news, though- on 10.6, you have access to a whole fleet of networksetup switches that will handle creating the 802.1x profiles for you. Check the networksetup man page for the details.

You should still be able to use the functions from my script that relate to requesting the certificate from your certificate authority. Just replace the parts that create the mobileconfig file with the appropriate networksetup commands.

Adeaso
New Contributor

NKalister,
That script that you wrote is awesome for getting a user cert. Is there a way the script can be modified to retrieve the "machine" cert from the AD CA? Thanks!!!!!

alexjdale
Valued Contributor III

This is a pretty old thread so I am not sure if you will get a response, but yes, the CSR would need to be modified to get a machine cert (using the machine's hostname and using the right cert template on your CA). We used to use something like this method, but switched over to configuration profiles requesting the cert instead, which I strongly recommend.

nkalister
Valued Contributor

yeah, if you're bound to AD, the RPC/DCE config profile is much easier simpler to implement.
If you're not bound to AD and you don't want to use SCEP, then a script like this is still useful. I'm still using an updated version of this script, for instance.
To get a machine cert, you'd need to change the certificate template on the CA. Changing the CSR didn't seem to be necessary in my environment, but there's really no reason NOT to use the machine's name there.

Emmert
Valued Contributor

Offtopic, but "account deleted" is an awesome username.

Swift
New Contributor II

If anyone is interested, here's a version of the script for connecting to 802.1X Wi-Fi - that uses the AD computer credentials to get the machine certificate.

#!/bin/bash
#
# Generate and install a mobileconfig profile that auto-joins a 802.1X
# Wi-Fi network using the AD computer credentials for the connection.
# - No user credentials need to be entered.
# - Wi-Fi works at the Login Window
# Mark J Swift
#
# Almost completely based on the script by nkalister
#
# Usage
#   1. Connect to a network that can access your CA server
#   2. Install and trust your CA certificate and certificate chain
#   3. Run this script
#   4. Sometimes you have to restart
#
# REFERENCES: 
#   nkalister - https://jamfnation.jamfsoftware.com/discussion.html?id=3387
#   cdown - https://gist.github.com/cdown/1163649
# ---

# -- BEGIN CUSTOMISE --
CA_URL="https://yourcaserver/certsrv"  # Change to the address of your CA server
CERT_TEMPLATE="Mac-Computer"           # Change to the name of the computer cert template
SSID_STR="yourSSID"                    # Change to your Wi-Fi SSID
PROXY_TYPE="Auto"                      # Change to Auto or None
# -- END CUSTOMISE --

# ---

if [ "$(whoami)" != "root" ]
then
  echo >&2 "This script needs to be run with root privileges"
  exit 0
fi

# ---

# urlencode function - thanks to https://gist.github.com/cdown/1163649
f_urlencode() {
    # urlencode <string>

    local length="${#1}"
    for (( i = 0; i < length; i++ )); do
        local c="${1:i:1}"
        case $c in
            [a-zA-Z0-9.~_-]) printf "$c" ;;
            *) printf '%s' "$c" | xxd -p -c1 |
                   while read c; do printf '%%%s' "$c"; done ;;
        esac
    done
}

# Define a browser user agent
BROWSER_AGENT="Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5"

# Temporary File location
TEMP_DIR="$(mktemp -dq /tmp/XXXXXXXX)"

# Get Computer AD credentials
AD_TRUST_ACCOUNT=$(echo "show com.apple.opendirectoryd.ActiveDirectory" | scutil | grep "TrustAccount" | cut -d":" -f 2- | sed "s|^[ ]*||;s|[ ]*$||")
if test -n "${AD_TRUST_ACCOUNT}"
then
  AD_DOMAINNAME_FLAT=$(echo "show com.apple.opendirectoryd.ActiveDirectory" | scutil | grep "DomainNameFlat" | cut -d":" -f 2- | sed "s|^[ ]*||;s|[ ]*$||")
  AD_TRUST_PASSWORD=$(security find-generic-password -w -s "/Active Directory/${AD_DOMAINNAME_FLAT}" /Library/Keychains/System.keychain)
  if test -z "${AD_TRUST_PASSWORD}"
  then
    echo >&2 "Can't get AD trust password"
    exit 0
  fi
else
  echo >&2 "Can't get AD Trust Account"
  exit 0
fi

# AD computer name (without the trailing dollar sign)
AD_COMPUTER_NAME=$(echo ${AD_TRUST_ACCOUNT} | sed "s|$$||")

# Define (temporary) file names
KEY_FILE="${TEMP_DIR}/${AD_COMPUTER_NAME}.key"
CSR_FILE="${TEMP_DIR}/${AD_COMPUTER_NAME}.csr"
PEM_FILE="${TEMP_DIR}/${AD_COMPUTER_NAME}.pem"
PK12_FILE="${TEMP_DIR}/${AD_COMPUTER_NAME}.p12"
MOBILECONFIG_FILE="${TEMP_DIR}/${AD_COMPUTER_NAME}.mobileconfig"

# Generate a CSR file
/usr/bin/openssl req -new -batch -newkey rsa:2048 -nodes -outform PEM -keyout "${KEY_FILE}" -out "${CSR_FILE}"

# URL encode the CSR
ENCODED_CSR="$(f_urlencode "$(cat "${CSR_FILE}")")"

# Post the Certificate request to the Web Enrollment page 
REQ_HTML_RESPONSE="$(curl --connect-timeout 10 -k --ntlm -u ${AD_TRUST_ACCOUNT}:${AD_TRUST_PASSWORD} -d CertRequest=${ENCODED_CSR} -d SaveCert=yes -d Mode=newreq -d CertAttrib=CertificateTemplate:"${CERT_TEMPLATE}" ${CA_URL}/certfnsh.asp)"

if test -z "${REQ_HTML_RESPONSE}"
then
  # Display an error status
  echo >&2 "REQUEST FAILED (it probably timed out)"
  exit 0
fi

# We need the ReqID from the server response
REQ_ID=$(echo "${REQ_HTML_RESPONSE}" | grep "ReqID=" | grep -v "Renewal" | sed "s|(.*ReqID=)(.*)|2|;s|(^[0-9]*)(.*)|1|" | head -n 1)

if test -z "${REQ_ID}"
then
  # Display an error status
  echo >&2 "${REQ_HTML_RESPONSE}"
  echo >&2
  echo >&2 "REQUEST FAILED (read the server response above)"
  exit 0

else
  # Pull the computer cert down in PEM format
  curl --connect-timeout 10 -k -o "${PEM_FILE}" -A "${BROWSER_AGENT}" --ntlm -u ${AD_TRUST_ACCOUNT}:${AD_TRUST_PASSWORD} "${CA_URL}/certnew.cer?ReqID=${REQ_ID}&Enc=b64"

fi

# Pack the computer cert and private key into a PKCS12 file
openssl pkcs12 -export -in "${PEM_FILE}" -inkey "${KEY_FILE}" -out "${PK12_FILE}" -name "${AD_TRUST_ACCOUNT}" -passout "pass:pass"

# Generate some unique uuids for the payloads within the mobileconfig
CONFIG_PAYLOAD_UUID=$(uuidgen)
CERT_PAYLOAD_UUID=$(uuidgen)
WIFI_PAYLOAD_UUID=$(uuidgen)

# Get full domain name
AD_DOMAINNAME_DNS=$(echo "show com.apple.opendirectoryd.ActiveDirectory" | scutil | grep "DomainNameDns" | cut -d":" -f 2- | sed "s|^[ ]*||;s|[ ]*$||")

# Create the mobile config
cat << HEREDOC > "${MOBILECONFIG_FILE}"
<?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>PayloadContent</key>
  <array>
    <dict>
      <key>Password</key>
      <string>pass</string>
      <key>PayloadCertificateFileName</key>
      <string>${AD_COMPUTER_NAME}.p12</string>
      <key>PayloadContent</key>
      <data>
      </data>
      <key>PayloadDescription</key>
      <string>Configures certificate settings.</string>
      <key>PayloadDisplayName</key>
      <string>${AD_COMPUTER_NAME}.p12</string>
      <key>PayloadIdentifier</key>
      <string>${AD_COMPUTER_NAME}.${AD_DOMAINNAME_DNS}.AutoJoin8021XWiFi.com.apple.security.pkcs12.${CERT_PAYLOAD_UUID}</string>
      <key>PayloadType</key>
      <string>com.apple.security.pkcs12</string>
      <key>PayloadUUID</key>
      <string>${CERT_PAYLOAD_UUID}</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
    </dict>
    <dict>
      <key>AutoJoin</key>
      <true/>
      <key>EAPClientConfiguration</key>
      <dict>
        <key>AcceptEAPTypes</key>
        <array>
          <integer>13</integer>
        </array>
      </dict>
      <key>EncryptionType</key>
      <string>WPA</string>
      <key>HIDDEN_NETWORK</key>
      <true/>
      <key>IsHotspot</key>
      <false/>
      <key>PayloadCertificateUUID</key>
      <string>${CERT_PAYLOAD_UUID}</string>
      <key>PayloadDescription</key>
      <string>Configures Wi-Fi settings</string>
      <key>PayloadDisplayName</key>
      <string>WiFi</string>
      <key>PayloadIdentifier</key>
      <string>${AD_COMPUTER_NAME}.${AD_DOMAINNAME_DNS}.AutoJoin8021XWiFi.com.apple.wifi.managed.${WIFI_PAYLOAD_UUID}</string>
      <key>PayloadType</key>
      <string>com.apple.wifi.managed</string>
      <key>PayloadUUID</key>
      <string>${WIFI_PAYLOAD_UUID}</string>
      <key>PayloadVersion</key>
      <real>1</real>
      <key>ProxyType</key>
      <string>${PROXY_TYPE}</string>
      <key>SSID_STR</key>
      <string>${SSID_STR}</string>
            <key>SetupModes</key>
      <array>
        <string>System</string>
      </array>
    </dict>
  </array>
  <key>PayloadDisplayName</key>
  <string>${AD_COMPUTER_NAME} Wi-Fi</string>
  <key>PayloadIdentifier</key>
  <string>${AD_COMPUTER_NAME}.${AD_DOMAINNAME_DNS}.AutoJoin8021XWiFi</string>
  <key>PayloadRemovalDisallowed</key>
  <false/>
  <key>PayloadScope</key>
  <string>System</string>
  <key>PayloadType</key>
  <string>Configuration</string>
  <key>PayloadUUID</key>
  <string>${CONFIG_PAYLOAD_UUID}</string>
  <key>PayloadVersion</key>
  <integer>1</integer>
</dict>
</plist>
HEREDOC

# Insert the pkcs12 file into the mobileconfig
/usr/libexec/PlistBuddy -c "Import PayloadContent:0:PayloadContent ${PK12_FILE}" "${MOBILECONFIG_FILE}"

# Remove any previous payload
profiles >/dev/null 2>&1 -R -p "${AD_COMPUTER_NAME}.${AD_DOMAINNAME_DNS}.AutoJoin8021XWiFi"

# Install the mobileconfig
profiles -I -F "${MOBILECONFIG_FILE}"

# permanently delete the temporary files
srm -fR "${TEMP_DIR}"

# You may not want to set up the wi-fi port

# Get Wi-Fi port (i.e. en1)
WIFI_PORT=$(networksetup -listallhardwareports | tr "
" ":" | sed "s|^[:]*||;s|::|;|g" | tr ";" "
" | grep "Wi-Fi" | sed "s|(.*Device:[ ]*)([^:]*)(.*)|2|")

# Turn Wi-Fi port on
networksetup -setairportpower ${WIFI_PORT} on

# Only allow admins to change the Wi-Fi settings
/usr/libexec/airportd prefs RequireAdminIBSS=YES RequireAdminNetworkChange=YES RequireAdminPowerToggle=YES

The script generates and installs a mobileconfig profile that auto-joins a 802.1X Wi-Fi network using the computer credentials for the connection. User credentials are not required.