I beg of you for SecureToken assistance.

horganj76
New Contributor II

Our prestage enrollment is set to create a hidden admin account and sets the account created via Setup Assistant to be a standard account. Here's the catch-22 I've run into:

  1. The hidden admin account will only get a SecureToken if it is the first account to log in.

  2. I can't escrow a bootstrap token to the hidden admin account, since the only other account on the system with a Securetoken is a standard user.

Has anyone else run into this issue? If so, what is your workflow?

14 REPLIES 14

AdamCraig
Contributor III

I have a policy that happens as part of the config process that prompts the user for their password and then uses this to add the admin account to FV2 and get them a secure token along with doing a bunch of other stuff.

Here are the relevant parts of that script:

#!/bin/bash

adminName=$4
adminPass=$5
loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )

if [[ -z "$loggedInUser" ]] || [[  "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
    echo "Failed to gather loggedInUser correctly"
    exit 1
else
    echo "loggedInUser is $loggedInUser"
fi
loggedInUID=$(id -u "$loggedInUser")

#### SET UP DISPLAY DIALOG FUNCTION
DisplayDialog(){
    local dialogText="$1"
    echo "Display Dialog: $dialogText"
    cmd="Tell app "System Events" to display dialog "$dialogText""
    /usr/bin/osascript -e "$cmd"
}
echo " "
echo "Checking Admin User"
adminCheck=$(id -u "$adminName")
if [[ "$adminCheck" == *"no such user"* ]] || [[ -z "$adminCheck" ]] ; then
    echo "admin user not installed"
    jamf policy -trigger installadmin0417
    sleep 5
fi

adminCheck=$(id -u "$adminName")
if [[ "$adminCheck" == *"no such user"* ]] || [[ -z "$adminCheck" ]] ; then
    echo "admin user still not installed"
    DisplayDialog "$adminName not installed. Exiting"
    exit 1
fi

echo " "
echo "Checking admin passsword"
adminPassCheck=$(/usr/bin/dscl /Local/Default -authonly "$adminName" "$adminPass")
if [[ -z "$adminPassCheck" ]]; then
    echo "Continue"
else
    echo "admin Password not set correctly"
    exit 1
fi

echo " "
echo "checking Filevault Status"
fvStatus=$(fdesetup status)
if [[ "$fvStatus" == *"FileVault is On."* ]] ; then
    echo "Verified Filevault Enabled"
else
    jamf policy -trigger catalina_fv -forceNoRecon
    sleep 5
    DisplayDialog "Filevault Not Yet Enabled. Once the rest of your setup is complete please restart your computer to enable filevault."
    exit 1
fi

### CHECK SECURE TOKEN STATUS:
if [[ $("/usr/sbin/diskutil" apfs listcryptousers / 2>&1) =~ "No cryptographic users" ]]; then
    tokenStatus="false"
else
    tokenStatus="true"    
fi
echo " "
echo "Token Status: $tokenStatus"

#################
# We should have everything we need from the system if it gets passed here
##################

secureTokenUserCheck() {
    local userToCheck="$1"
    if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$userToCheck" 2>&1) =~ "ENABLED" ]]; then
        userToken="true"
    else
        userToken="false"
    fi
    echo "$userToken"
}

addSecureToken() {
    local tokenUser="$1"
    local tokenUserPass="$2"
    local addUser="$3"
    local addUserPass="$4"
    echo "adding token to $addUser with credentials from $tokenUser"
    sysadminctl -adminUser "$tokenUser" -adminPassword "$tokenUserPass" -secureTokenOn "$addUser" -password "$addUserPass"
    tokenAdded=$(secureTokenUserCheck "$tokenUser")
    echo "Token Added to $tokenUser : $tokenAdded"
    diskutil apfs listcryptousers /
}

fileVaultUserCheck() {
    userToCheck="$1"
    fdeList=$(fdesetup list | grep "$userToCheck")
    echo "checking Filevault list $fdeList for $userToCheck"
    if [[ "$fdeList" == *"$userToCheck"* ]] ; then
        echo "FV2 Check for $userToCheck passed. Continuing"
    else
        DisplayDialog "$userToCheck not Filevault Enabled. Rectify this BEFORE you restart!"
        exit 1
    fi

}

###################
# Get get password from the user
#####################
echo " "
echo "Getting user password"

userPass="None"
loopCount=0
while [ $loopCount -lt 3 ]; do
    passCheck=$(/usr/bin/dscl /Local/Default -authonly "$loggedInUser" "$userPass")
    if [[ -z "$passCheck" ]]; then
        echo "Gathered Password"
    else
        echo "prompting user for Account Password"
        userPass=$(/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" /usr/bin/osascript<<END
        tell application "System Events"
        activate
        set the answer to text returned of (display dialog "Enter $loggedInUser's Current Account Password:" default answer "" with hidden answer buttons {"Continue"} default button 1)
        end tell
END
)
    fi
    passCheck=$(/usr/bin/dscl /Local/Default -authonly "$loggedInUser" "$userPass")
    if [[ -z "$passCheck" ]]; then
        echo "Continue"
        break

    else
        echo "Authorization failed"
        DisplayDialog "Password Check for $loggedInUser failed. Please Try again."
        ((loopCount++))
    fi
done
rm -f "$passLocation"

if [[ -n "$passCheck" ]] ; then
    DisplayDialog "$loggedInUser Password failed three times. Exiting"
    exit 1
fi

# create the plist file:
echo '<?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>Username</key>
<string>'$loggedInUser'</string>
<key>Password</key>
<string>'$userPass'</string>
<key>AdditionalUsers</key>
<array>
    <dict>
        <key>Username</key>
        <string>'$adminName'</string>
        <key>Password</key>
        <string>'$adminPass'</string>
    </dict>
</array>
</dict>
</plist>' > /tmp/fvenable.plist  ### you can place this file anywhere just adjust the fdesetup line below

# now enable FileVault
fdesetup add -i < /tmp/fvenable.plist

rm -r /tmp/fvenable.plist

########### MANAGE SECURE TOKENS
echo " "
echo "checking secure tokens"
adminToken=$(secureTokenUserCheck "$adminName")
userToken=$(secureTokenUserCheck "$loggedInUser")

if [[ "$tokenStatus" == "false" ]] ; then
    echo "No Secure Tokens, adding admin and logged in user to secure token"
    addSecureToken "$adminName" "$adminPass" "$loggedInUser" "$userPass"
    adminToken=$(secureTokenUserCheck "$adminName")
    userToken=$(secureTokenUserCheck "$loggedInUser")
fi

### CHECK SECURE TOKEN STATUS:
if [[ $("/usr/sbin/diskutil" apfs listcryptousers / 2>&1) =~ "No cryptographic users" ]]; then
    tokenStatus="false"
    echo "Secure token still not enabled. This should never happen. Failing"
    DisplayDialog "There has been an error enabling this computers encryption. If you see this error more than once please inform Helpdesk."
    exit 1
else
    tokenStatus="true"    
fi

## setting this variable to false so it exists later
demoteUser="false"
if [[ "$adminToken" = "true" ]] ; then
    tokenUser="$adminName"
    tokenPass="$adminPass"
    echo "admin user has a token, setting as tokenUser"
elif [[ "$userToken" = "true" ]] ; then
    tokenUser="$loggedInUser"
    tokenPass="$userPass"
    echo "logged in user is only one with a token, this is bad. But will be attempted to be fixed automatically right now."
    ### Make sure this user is an Admin
    if [[ $("/usr/sbin/dseditgroup" -o checkmember -m "$loggedInUser" admin / 2>&1) =~ "yes" ]]; then
        echo "$loggedInUser is an admin"
    else
        echo "$loggedInUser is not an admin"
        demoteUser="true"
        ##Promoting user to admin
        echo "promoting user to admin"
        dscl . -append /groups/admin GroupMembership "$loggedInUser"
    fi
    addSecureToken "$loggedInUser" "$userPass" "$adminName" "$adminPass"
    ## remove admin if it was added
    if [[ "$demoteUser" = "true" ]] ; then
        echo "demoting User from admin"
        dscl . -delete /groups/admin GroupMembership "$loggedInUser"
    fi
    adminToken=$(secureTokenUserCheck "$adminName")
    if [[ "$adminToken" = "false" ]] ; then
        echo "adding admin user failed"
        DisplayDialog "There has been an error enabling this computers encryption. If you see this error more than once please inform Helpdesk."
        exit 1
    else
        tokenUser="$adminName"
        tokenPass="$adminPass"
    fi
fi

if [[ "$adminToken" = "false" ]] ; then
    addSecureToken "$tokenUser" "$tokenPass" "$adminName" "$adminPass"
fi
if [[ "$userToken" = "false" ]] ; then
    addSecureToken "$tokenUser" "$tokenPass" "$loggedInUser" "$userPass"
fi

fileVaultUserCheck "$adminName"

exit 0

Other thing that I do, which may break this workflow, is that all users are created as local admin, and then they are demoted if they shouldn't be admins once the initial setup is complete. I plan on changing that once Jamf Connect gets their Laps Password feature fixed...

pitcherj
New Contributor III

Hey there @strayer,

Would you mind sharing what your catalina_fv trigger is doing?

I'm running into the same issue, have an M1 MacBook Air (first one on-site) and after doing some generic testing, discovered that there are no users with a Secure Token, so FileVault isn't happy.

Thanks for your time!

Have a great day!

Jacob

walt
Contributor III

i dont have an answer for existing secure token issues, are you using anything like jamf connect?

are you able to deploy the hidden admin account after the first user created account? Our current workflow is user account is created through DEP, they are prompted for FileVault and we demote to a standard user account once enrollment/DEPNotify is completed. I haven't had issues with that process...so perhaps just another approach/perspective.

AdamCraig
Contributor III

@pitcherj 1cc4945b64c649ca88d7dd19818125d9
catalina_fv is pretty basic. but basically turns on Filevault and asks the user to restart. It's mostly a backup for when automatic filevault enabling fails

cvangorp
New Contributor III

@pitcherj I too had issues with securetoken and bootstrap on M1 mac. Not with filevault but with software update. Have to have a bootstrap enabled user to run software Update!
I ended up changing my DEP process to create a hidden local admin (won't be used) then on enrollment complete run a script that creates runs Jamf createaccount to create a local admin that our techs will use to setup the mac for the user. Since it will be the first user to login (interactively) then it gets the securetoken and bootstrap.

pitcherj
New Contributor III

@cvangorp Interestingly, our DEP process already creates a hidden local admin, so perhaps that create account portion is all I need.

Thanks for pointing me in the right direction!

I'll report back with my results.

Have a great day!

Jacob

pitcherj
New Contributor III

@cvangorp

I'm going to do some more testing, but I just did a basic enrollment with a new script using the create account command, but it didn't create a local admin account, it created a standard user.

Neither the hidden local admin or the standard user has a secure token, though.

cvangorp
New Contributor III

@pitcherj jamf createaccount -username {username} -realname {my real name} -password {mypassword} -admin

The -admin will set the account to an admin. I found the yes setup assistant can create the admin account but that account does not get secure or bootstrap token. https://travellingtechguy.blog/filevault-securetoken-and-bootstrap-in-macos-11-0-1-big-sur/

pitcherj
New Contributor III

Is it possible this issue is being triggered because I am using the "Create a local administrator account before the Setup Assistant" option in my PreStage Enrollment?

imy
New Contributor III

@pitcherj Did you ever find a solution? I'm having the same issue, and I also suspect it's caused by creating the local admin account before Setup Assistant.

What's odd is that it works (in that the admin account being created as part of PreStage as a secure token ) about half the time,and the other half it does not. I have not yet figured out what dictates which way it will go. There seems to be some race condition here.

One thing I have been doing before each test is the following:
1. Get into Recovery by holding down the power button
2. When there, open Terminal
3. Type "resetpassword"
4. From the menu, choose to Erase the Mac.

I have found that if you fail to do this, your admin account may stick around even after deleting the partitions/volumes. This seems to be the way to fully wipe out everything. If the account sticks around, it seems that it likely won't get a token

I'm currently trying the following:
1. In PreStage, give the admin account a different name. So, if your normal admin account is called "admin", call this one "tempAdmin"
2. Create the local admin via a policy, with the user name of "admin", and set this to happen on enrollment completion

It's running through the OS install now, so I'll know in about an hour if this worked or not. The downside of this approach is that your admin account won't be a FileVault user, but I'm trying to get away from having a local admin account anyway, so this works for me. You will still be able to get in with the recovery key of course.

pitcherj
New Contributor III

I did!

It was weird issue with the M1 firmware.

Had to do a restore, not a revive, from apple configurator.

Jacob

imy
New Contributor III

@pitcherj So you're saying if I do the following:
1. Wipe the Mac by doing a restore from Apple Configurator
2. Leave my PreStage creating the standard admin account
That it will create that admin account with the secure token every time? I'm curious if you have done multiple runs with this, as my experience with my current method is that it works half the time.

pitcherj
New Contributor III

Yup.

imy
New Contributor III

@pitcherj It's been a week for me, but I think I got all of my bugs worked out.

For anyone that might run into the same issues, here is what I found:

The account I was creating in PreStage was the same as the one that is set in the user-initiated enrollment section. Why I had it set this way I don't know, but I have since corrected it by changing the user-initiated account to something else. Why I kept getting the variable behavior, I think, was due to a race condition. If I got in fast enough, the flag for this account to not get a secure token was not yet set. See https://travellingtechguy.blog/filevault-securetoken-and-bootstrap-in-macos-11-0-1-big-sur/ that @cvangorp already noted for more details on this

The account created in PreStage won't get a token. This isn't a problem, because you can just use a policy that creates another admin account.

Now with all that sorted, I was still having issues, ie my admin account getting created after the setup assistant was done was still not getting a secure token. After trying so many things I lost count, I tried moving the Mac I was testing on to a different PreStage to try some other stuff with the account. When that didn't work out I moved it back to my original PreStage, and ever since it has worked perfectly. Why this makes a difference I don't know; my guess is that something got messed up either on the Jamf or Apple side, and moving it out and back into the PreStage refreshed it all.

This wasn't it at all. Back when CVE-2017-13872 was an issue I packaged @rtrouton 's script here and left it in place ever since for all devices, as I figured it would be a good precaution. The issue here was that if that policy ran before my policy to make the admin account, that admin account didn't get the secure token. This jives with my results while trying to find a user with a secure token; root showed as not having a secure token but it's UUID was listed when running

diskutil apfs listcryptousers /

and

fdesetup list -extended

For now I'm keeping the root block in place, but running it after the admin is created. This gives a secure token

I still have yet to test FV and verify I'm getting bootstrap, but when I did get secure token before those worked so hopefully that remains true.