Select Page

Context

I work as a MacAdmin in an education environment for a public school system in Switzerland. We manage approximately 2500 Macs in a mixed macOS-Linux-Windows environment.

The specificity of our Macs is that none of them are attributed to a specific person. We are bound to an Active Directory and anyone that has an existence in the Active Directory (teachers, students or staff) can log in to any computer.

These Macs are managed with an MDM, in our case JAMF Pro.

Secure token conundrum

In that context, in order to manage our Macs we create a local admin account via the JAMF prestage. This account is the only local account, as all other accounts are network accounts.

On Apple Silicon Macs, the secure token has become even more important than just being necessary to activate FileVault. It is now necessary for tasks such as Erase all Contents and Settings, or to update or upgrade macOS.

However, we run into what seems to be a conundrum. The local admin account, due to the way it is created (in the JAMF prestage) does not get a secure token. As the only local account, it would get a secure token the first time we log in with this account, but this never happens as it is a management-only account. Moreover, since no-one has a secure token, no bootstrap token can be escrowed, and therefore no-one will ever get a secure token.

That’s quite a bummer.

Granting a secure token

Apple provides a way to grant a secure token to a user using the following command:

sysadminctl -adminUser ADMIN -adminPassword ADMINPASSWORD -secureTokenOn USER -password USERPASSWORD

In this command, ADMIN is a user with administrative privileges and USER is a user that needs to be granted the secure token. However, in order for this command to work, the ADMIN user must have the secure token enabled, but as stated above, for the moment, no-one on our computers holds a secure token.

Well it turns out that if no-one holds a secure token, any user with administrative privileges can grant itself a secure token. We can use the same command as above but with a little twist:

sysadminctl -adminUser ADMIN -adminPassword ADMINPASSWORD -secureTokenOn ADMIN -password ADMINPASSWORD

We tweaked the command to actually use the ADMIN user to grant itself the secure token.

We can use the following command to verify that the secure token was correctly enabled for our ADMIN account:

/usr/sbin/sysadminctl -secureTokenStatus ADMIN

Here is a script that does all of these tasks, also available on GitHub:

#!/bin/bash
#
# Activate secure token for management account, if needed
#
# By Fabien Conus - 17/01/23


# Arguments passed by JAMF
# Parameter 4: the name of the management account
# Parameter 5: the password for the local admin account encoded in base 64
secureuser=${4}
b64pass=${5}

# Decode the base 64 encoded password
password="$(echo "$b64pass" | base64 -d)"

# Check if the account provided exists and is admin
check="$("/usr/sbin/dseditgroup" -o checkmember -m $secureuser admin / 2>&1)"
if [[ "$check" =~ "Unable" ]]; then
	echo "The account \"$secureuser\" does not exist on this computer."
	exit 1
elif [[ "$check" =~ "NOT" ]]; then
	echo "The account \"$secureuser\" is not an admin on this computer."
	exit 2
fi

# Check if the provided account already has a secure token
if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$secureuser" 2>&1) =~ "ENABLED" ]]; then
	echo "Secure token is already enabled for user \"$secureuser\"."
	exit 0
else
	echo "Secure token is disabled for user \"$secureuser\". Let's activate it."
fi

# Since our only local acount does not have the secute token enabled, let's activate it
output=$(sysadminctl -adminUser "$secureuser" -adminPassword "$password" -secureTokenOn "$secureuser" -password "$password" 2>&1 | grep -c "Error")

# Check if an error occured
if [ $output -eq 0 ]; then
	echo "Token granted !"
else
	echo "Something went wrong. Unable to grant secure token to account $secureuser."
	exit 1
fi

# Verify that the secute token is enabled
securetokencheck="$(/usr/sbin/sysadminctl -secureTokenStatus "$secureuser" 2>&1)"
if [[ "$securetokencheck" =~ "DISABLED" ]]; then
	echo "$securetokencheck"
	echo "Something went wrong."
	exit 3
fi

exit 0

Escrowing the bootstrap token

Now that the management account as the secure token, it is possible to escrow the bootstrap token to the server. This is quite simply done with the profiles command. The only trick is that it works in an interactive way since you have to provide the admin password. In order to automate it and allow it to run in the background, I am using expect to parse the output and provide the necessary information. Here is a script that does all of that (also available on GitHub):

#!/bin/bash
#
# Escrow the bootstrap token
# using a secute token enabled account
#
# By Fabien Conus - 16/01/2023

# Arguments passed by JAMF
# Parameter 4: the name of the management account
# Parameter 5: the password for the local admin account encoded in base 64
secureuser=${4}
b64pass=${5}

# Decode the base 64 encoded password
password="$(echo "$b64pass" | base64 -d)"

# Create an expect command to escrow the bootstrap token using the profiles command
echo "*** Escrowing bootstrap token"

command="spawn /usr/bin/profiles install -type bootstraptoken; expect \"Enter the admin user name:\"; send -- \"$secureuser\r\"; expect \"Enter the password for user '$secureuser':\"; send -- \"$password\r\"; expect eof"

# Execute the expect command
expect -c "$command"

# Check if the bootstrap token was correctly escrowed
echo "*** Checking if bootstrap token was correctly escrowed: "

check="$(profiles status -type bootstraptoken | grep "to server" | awk -F":" '{ gsub(/ /,""); print $3}')"

if [[ "$check" == "NO" ]]; then
	echo "*** Failed to escrow bootstrap token"
	echo "$check"
	exit 1
else
	echo "*** Bootstrap token successfully escrowed"
	echo "$check"
fi

exit 0

Conclusion

In an environment where no-one has a secure token, the sysadminctl command can tricked into granting a secure token to an admin account. One can then use the profiles command to escrow the bootstrap token.

Once the bootstrap token is escrowed, each user that logs in will be granted the secure token.

References

Traveling Tech Guy has a great write-up on secure tokens and bootstrap tokens: FileVault, SecureToken and Bootstrap in macOS 11.0.1 Big Sur

I was greatly inspired by this script, also by Traveling Tech Guy