macOS Mojave updatepreBoot Yields A System Prompt

nwagner
Contributor

I searched all over for this specific problem and I can't seem to find anything close. Feel free to redirect me

I've got a LaunchD that calls a script to do a bunch of checks before calling a JAMF policy that grants the logged in user a secure token for filevault.

The problem I'm having is that I need to update preboot as part of the JAMF policy, but when the script gets there I get this
b462060e031e41b6b4e58f008765545a

I'm not quite sure why this is happening. JAMF essentially runs as root. I've even tested running the command:

diskutil apfs updatePreboot /

As root just to check if the same prompt would happen, and it still happens.

I'm trying to get an alpha version of what we will be running soon in testers hands but that system prompt is killing me. Here's all the code, please excuse the ugly code, its still pre-alpha for right now.

For reference here's the launchD that calls the first script:

<?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>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>com.spi.GrantUserToken</string>
    <key>ProgramArguments</key>
    <array>
        <string>sh</string>
        <string>/usr/local/spi/bin/GrantUserToken</string>
    </array>
    <key>UserName</key>
    <string>root</string>
    <key>RunAtLoad</key>
    <false/>
    <key>StartInterval</key>
    <integer>30</integer>
    <key>ExitTimeOut</key>
    <integer>120</integer>
    <key>Debug</key>
    <true/>
</dict>
</plist>

Here's the script that the LaunchD calls:

#!/bin/bash
CurrentUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
chkLocal=$(id -u ${CurrentUser})
tStamp=$(date +%x_%H:%M:%S)
sName=$(basename "$0")
logStamp="${sName} | ${tStamp}"
tokenAddDate=/usr/local/spi/etc/$CurrentUser.txt
lsgutLog=/usr/local/spi/log/LocalScriptGrantUserToken.log
pDelta='false'

# Adding the log file if it doesn't exist
# wish I didn't do this outside of a function
if [[ ! -f $lsgutLog ]]
then
    if [[ ! -d /usr/local/spi/log ]]
    then
        echo "${logStamp} | Creating a log path"
        mkdir /usr/local/spi/log
    fi
    echo "${logStamp} | Creating a log"
    touch $lsgutLog
fi

# Checking for /spi/etc path
if [[ ! -d /usr/local/spi/etc/ ]]
then
    echo "${logStamp} | Creating a etc path"
    mkdir /usr/local/spi/etc
fi

chkJAMFStatus(){
    # not called by any other function - nneds to be in main
    # make sure we can connect to JAMF
    echo "Running chk JAMF Status"
    if [[ $(/usr/local/jamf/bin/jamf checkJSSConnection | grep 'available') =~ "The JSS is available." ]]
    then
        jamfStatus='true'
        echo "${logStamp} | JAMF connection OK"
        echo "${logStamp} | Check FDE Status on this machine"
        chkFVStatus
    else
        jamfStatus='false'
        echo "${logStamp} | Cannot connect to JAMF"
        echo "${logStamp} | Exiting Now"
        exit 0
    fi
}

chkFVStatus(){
    # Called by checkJAMFStatus
    # Check if FileVault is Enabled
    echo "${logStamp} | Running chk FV Status"
    if [[ $("/usr/bin/fdesetup" status 2>&1) =~ "FileVault is On." ]]
    then
        fvStatus='true'
        echo "${logStamp} | FileVault is On"
        echo "${logStamp} | Check for console user"
        chKLoginStatus
    else
        fvStatus='false'
        echo "${logStamp} | FileVault is Off"
        echo "${logStamp} | Exiting Now"
        exit 0
    fi
}

chKLoginStatus(){
    # Called by chkFVStatus
    # Check if someone is logged in
    chkFinder=$(pgrep -x "Finder")
    chkDock=$(pgrep -x "Dock")
    if [ "$chkFinder" != "" ]  && [ "$chkDock" != "" ] && [ "$CurrentUser" != "_mbsetupuser" ]
    then
        if [[ "$CurrentUser" == "" ]]
        then
            loginStatus='false'
            echo "${logStamp} | Nobody is logged in"
            echo "${logStamp} | Exiting Now"
            exit 0
        else
            loginStatus='true'
            echo "${logStamp} | ${CurrentUser} is logged in"
            echo "${logStamp} | Check ${CurrentUser} netowrk status"
            chkLocalUsers
        fi
    fi
}

chkLocalUsers(){
    # Called by chKLoginStatus
    # see if the current user is a local user
    if [[ $chkLocal -lt 900 ]]
    then
        localUsr='true'
        echo "${logStamp} | ${CurrentUser} is not a network user, UID is ${chkLocal}"
        echo "${logStamp} | Exiting Now"
        exit 0
    else
        localUsr='false'
        echo "${logStamp} | User is a netowrk user"
        echo "${logStamp} | Check ${CurrentUser} Token"
        chkUserToken
    fi
}

chkUserToken(){
    # Called by chkLocalUsers
    if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$CurrentUser" 2>&1) =~ "ENABLED" ]]; then
        userToken='true'
        echo "${logStamp} | ${CurrentUser} already has a token"
        echo "${logStamp} | Check ${CurrentUser} password delta"
        chkUserPassDelta
    else
        userToken='false'
        echo "${logStamp} | ${CurrentUser} needs a token"
        echo "${logStamp} | ${CurrentUser} Call for JAMF"
        callJAMFPolicy
    fi
}

chkUserPassDelta(){
    # Called by chkUserToken
    if [[ -f $tokenAddDate ]]
    then
        # get Last Password change from DSCL
        MSLastPWD=`dscl "/Active Directory/SOMEPLACE/All Domains" -read /Users/${CurrentUser} | grep -i SMBPasswordLastSet | cut -d ' ' -f 2 | sed q`
        # get today's date in Unix time
        todayUnix=`date "+%s"`
        # Convert Last Password Change date into Unix Time
        lastPWDUnix=`expr $MSLastPWD / 10000000 - 11644473600`
        # Calculate Difference between Today's Date and Last Changed Date
        diffUnix=`expr $todayUnix - $lastPWDUnix`
        # Get the Unix Date when the user last got a token from tokenAddDate
        tokenDay=$( tail -n 1 $tokenAddDate  | awk -F'on ' '{print $2}' )
        # Get the difference between todays date and when the current user last got a token
        diffTokenTime=`expr $todayUnix - $tokenDay`
        # check if the user has changed their password in the last 86400 seconds 
        # and they last got issued a token more than 86400 seconds ago
        # Have to do it in seconds cause bash doesn't do floats at all
        if [[ $diffUnix -lt 86400 ]] && [[ $diffTokenTime -gt 86400 ]]
        then
            pDelta='true'
            echo "${logStamp} | ${CurrentUser} needs a token or a sync"
            echo "${logStamp} | ${CurrentUser} Call for JAMF"
            callJAMFPolicy
        else
            pDelta='false'
            echo "${logStamp} | ${CurrentUser} is good to go."
            echo "${logStamp} | Exiting now"
            exit 0
        fi
    else
        pDelta='true'
        echo "${logStamp} | ${CurrentUser} has no TokenAdd File"
        echo "${logStamp} | ${CurrentUser} Call for JAMF"
        callJAMFPolicy
    fi
}

callJAMFPolicy(){
    # check if any of the checks failed, if any one did then exit, otherwise phone home
    if [[ $jamfStatus == 'true' ]] && [[ $fvStatus=='true' ]] && [[ $localUsr == 'false' ]] && [[ $loginStatus == 'true' ]] && [[ $pDelta == 'true' ]]
    then
        #Go for JAMF policy event call grantToken
        echo "${logStamp} | All checks passed"
        echo "${logStamp} | Caliing JAMF"
        /usr/local/jamf/bin/jamf policy -event "grantToken"
    else
        echo "${logStamp} | JAMF Status is ${jamfStatus}"
        echo "${logStamp} | FileVault is on ${fvStatus}"
        echo "${logStamp} | User has a Token ${userToken}"
        echo "${logStamp} | User is local ${localUsr}"
        echo "${logStamp} | Login status is ${loginStatus}"
        echo "${logStamp} | User Toekn is in sync ${pDelta}"
        echo "${logStamp} | Failed one or more checks"
        exit 0
    fi
}

main(){
    chkJAMFStatus
    sleep 5
}

main > $lsgutLog 2>&1

And here's the script that the JAMF policy runs if it gets called:

#!/bin/bash
# JAMF VARS ONLY
AdminUser=$4
AdminPass=$5
# START MY VARIABLES
CurrentUser=$(/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }')
todayUnix=`date "+%s"`
tokenAddDate=/usr/local/spi/etc/$CurrentUser.txt
tokenLog=/usr/local/spi/log/$CurrentUser.log
tStamp=$(date +%x_%H:%M:%S)
sName=$(basename "$0")
logStamp="${sName} | ${tStamp}"
pDelta='false'
# END MY VARIABLES

# Adding the log file if it doesn't exist
if [[ ! -f $tokenLog ]]
then
    if [[ ! -d /usr/local/spi/log ]]
    then
        echo "${logStamp} | Creating a log path"
        mkdir /usr/local/spi/log
    fi
    echo "${logStamp} | Creating a log"
    touch $tokenLog
fi

# Checking for /spi/etc path
if [[ ! -d /usr/local/spi/etc/ ]]
then
    echo "${logStamp} | Creating a etc path"
    mkdir /usr/local/spi/etc
fi

chkAdminCred(){
    # Check if we passed the parameters in at all
    echo "${logStamp} | Running chk Admin Cred"
    if [[ "${AdminUser}" == "" ]] || [[ "${AdminPass}" == "" ]]
    then
        echo "${logStamp} | Admin User or Pass undefined, go back and set up in parameters 4 and 5."
        exit 1
    fi
    if [[ "${AdminUser}" == "${CurrentUser}" ]]
    then
        echo "${logStamp} | Admin user cannot be the same as the current user"
        exit 2
    fi
}

thatThing(){
    while :; do # Loop until valid input is entered or Cancel is pressed.
    CurrentUserPass="$(sudo -u $CurrentUser /usr/bin/osascript -e 'tell application "System Events" to display dialog "Please enter your current password" default answer "" with title "FileVault Configuration" with text buttons {"Submit"} default button 1 with hidden answer' -e 'text returned of result')"
    if (( $? )); then exit 50; fi  # Abort, if user pressed Cancel.
    if [[ -z "$CurrentUserPass" ]]; then
        # The user left the project name blank.
        sudo -u $CurrentUser osascript -e 'Tell application "System Events" to display alert "You must enter a password; please try again." as warning' >/dev/null
        # Continue loop to prompt again.
    else
        # Valid input: exit loop and continue.
        break
    fi
    done
}

JRRToken(){
    echo "${logStamp} | Running JRR Token"
    # Prompt for password
    echo "${logStamp} | Promting for password"
        thatThing
    #Checking to see if user has changed their password in less than a day
    if [[ ${pDelta} == "true" ]]
    then
        # Remove user from FV Token Holders redirect ALL output to null
        echo "${logStamp} | Removing user Token"
        sysadminctl -adminUser $AdminUser -adminPassword $AdminPass -secureTokenOff $CurrentUser -password $CurrentUserPass > /dev/null 2>&1
        sleep 1
    fi
    # Add (or re-add) user to FV Token Holders redirect ALL output to null
    echo "${logStamp} | Adding user Token"
    sysadminctl -adminUser $AdminUser -adminPassword $AdminPass -secureTokenOn $CurrentUser -password $CurrentUserPass > /dev/null 2>&1
    # Mark the data in Unix time when this users token was added
    # Always adding the latest change to the end of the text file
    if [[ ! -f ${tokenAddDate} ]]
    then
        echo "${logStamp} | creating file ${tokenAddDate}"
        touch $tokenAddDate
        chown root:wheel $tokenAddDate
        chmod 755 $tokenAddDate
        echo "${logStamp} | adding UNIX time to ${tokenAddDate}"
        echo "${CurrentUser} on ${todayUnix}" >> $tokenAddDate
    else
        echo "${logStamp} | adding UNIX time to ${tokenAddDate}"
        echo "${CurrentUser} on ${todayUnix}" >> $tokenAddDate
    fi
    # Update preboot
    # This could be long I may want to add a loop or something
    echo "${logStamp} | Updating preboot"
    diskutil apfs updatepreBoot /
    sleep 5
    echo "${logStamp} | End JRR Token"
}

chkUserPassDelta(){
    # Called by chkUserToken
    if [[ -f $tokenAddDate ]]
    then
        # get Last Password change from DSCL
        MSLastPWD=`dscl "/Active Directory/SOMEPLACE/All Domains" -read /Users/${CurrentUser} | grep -i SMBPasswordLastSet | cut -d ' ' -f 2 | sed q`
        # get today's date in Unix time
        todayUnix=`date "+%s"`
        # Convert Last Password Change date into Unix Time
        lastPWDUnix=`expr $MSLastPWD / 10000000 - 11644473600`
        # Calculate Difference between Today's Date and Last Changed Date
        diffUnix=`expr $todayUnix - $lastPWDUnix`
        # Get the Unix Date when the user last got a token from tokenAddDate
        tokenDay=$( tail -n 1 $tokenAddDate  | awk -F'on ' '{print $2}' )
        # Get the difference between todays date and when the current user last got a token
        diffTokenTime=`expr $todayUnix - $tokenDay`
        # check if the user has changed their password in the last 86400 seconds 
        # and they last got issued a token more than 86400 seconds ago
        if [[ $diffUnix -lt 86400 ]] && [[ $diffTokenTime -gt 86400 ]]
        then
            pDelta='true'
            echo "${logStamp} | ${CurrentUser} needs a token or a sync"
            echo "${logStamp} | ${CurrentUser} lets sync that token"
            JRRToken
        else
            pDelta='false'
            echo "${logStamp} | ${CurrentUser} is good to go."
            echo "${logStamp} | Exiting now"
            exit 0
        fi
    else
        pDelta='true'
        echo "${logStamp} | ${CurrentUser} has no TokenAdd File"
        echo "${logStamp} | ${CurrentUser} time to make one"
        JRRToken
    fi
}

chkUserToken(){
    if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$CurrentUser" 2>&1) =~ "ENABLED" ]]; then
        userToken="true"
        echo "${logStamp} | ${CurrentUser} already has a token."
        echo "${logStamp} | Check ${CurrentUser} password delta"
        chkUserPassDelta
    else
        userToken="false"
        echo "${logStamp} | ${CurrentUser} needs a token."
        JRRToken
    fi
}

main(){
    chkAdminCred  > $tokenLog 2>&1
    wait $!
    sleep 5
    chkUserToken  > $tokenLog 2>&1
    wait $!
    sleep 5
}

main

Also, calling sometimes this:

sudo -u $CurrentUser /usr/bin/osascript -e 'tell application "System Events" to display dialog blah blah'

Can result in an odd error, and I'm not 100% sure on how to fix that. If I remove the 'sudo -u' I get a 610 error, and if I have it I get a 54 error. Sometimes that happens, but not all the time for some reason.

It's all a bit of a hot mess right now, so please excuse the ugly scripts.

If anyone has any ideas that would be great.

3 REPLIES 3

nwagner
Contributor

I'm dumb.

I figured out that there was a media restriction on the internal drive that was causing the issue with updatepreBoot.

I can't figure out how to delete this post so...

I am still seeing some weirdness with returning the text from an applescript prompt.

Here's the full line from the needless vomit of code above:

CurrentUserPass="$(sudo -u $CurrentUser /usr/bin/osascript -e 'tell application "System Events" to display dialog "Please enter your current password" default answer "" with title "FileVault Configuration" with text buttons {"Submit"} default button 1 with hidden answer' -e 'text returned of result')"

Should I be using EOF for this? Does that fix the 610 error about interaction as root, or the 54 error that is a weird file permission error that makes little to no sense in this context. Gotta love applescript.

Anyhoo, if someone has an answer for that or knows how to delete this post, i'd be much obliged for your troubles.

daniel_hayden
New Contributor III

I had similar problems with my osascript in my token script..I ended up putting a jamf helper before the password input and getting rid of the while/do and replacing it with until/done with a counter. When it ran on my end users it popped up like 20 windows and they did not input user info. I limited the user password attempts to 5 tries.

                # Prompt for password
                jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
                LOGO="/Library/Application Support/JAMF/Jamf.app/Contents/Resources/AppIcon.icns"
                LOGO_POSIX="$(/usr/bin/osascript -e 'tell application "System Events" to return POSIX file "'"$LOGO"'" as text')"
                PROMPT_TITLE="Enabling SecureToken for FileVault"
                PROMPT_MESSAGE="Enabling SecureToken for FileVault. Please enter your Mac Login password on the following prompt."
                FINISHED_MESSAGE="Secure Token has successfully been enabled for FileVault"
                FORGOT_PW_MESSAGE="You made five incorrect password attempts. SecureToken was not enabled."
                USER_ID=$(/usr/bin/id -u "$userName")
                L_ID=$USER_ID
                L_METHOD="asuser"


                # Display a branded prompt explaining the password prompt.
                echo "Alerting user $userName about incoming password prompt..."
                "$jamfHelper" -windowType "utility" -icon "$LOGO" -title "$PROMPT_TITLE" -description "$PROMPT_MESSAGE" -button1 "Next" -defaultButton 1 -startlaunchd &>/dev/null

                # Get the logged in user's password via a prompt.
                echo "Prompting $userName for their Mac password..."
                userPass="$(/bin/launchctl "$L_METHOD" "$L_ID" /usr/bin/osascript -e 'display dialog "Please enter the password you use to log in to your Mac:" default answer "" with title "'"${PROMPT_TITLE//"/\"}"'" giving up after 86400 with text buttons {"OK"} default button 1 with hidden answer with icon file "'"${LOGO_POSIX//"/\"}"'"' -e 'return text returned of result')"

                # Validate the user's password
                TRY=1
                until /usr/bin/dscl /Search -authonly "$userName" "$userPass" &>/dev/null; do
                    (( TRY++ ))
                    echo "Prompting $userName for their Mac password (attempt $TRY)..."
                    userPass="$(/bin/launchctl "$L_METHOD" "$L_ID" /usr/bin/osascript -e 'display dialog "Sorry, that password was incorrect. Please try again:" default answer "" with title "'"${PROMPT_TITLE//"/\"}"'" giving up after 86400 with text buttons {"OK"} default button 1 with hidden answer with icon file "'"${LOGO_POSIX//"/\"}"'"' -e 'return text returned of result')"
                    if (( TRY >= 5 )); then
                        echo "[ERROR] Password prompt unsuccessful after 5 attempts. Displaying "forgot password" message..."
                        /bin/launchctl "$L_METHOD" "$L_ID" "$jamfHelper" -windowType "utility" -icon "$LOGO" -title "$PROMPT_TITLE" -description "$FORGOT_PW_MESSAGE" -button1 'OK' -defaultButton 1 -startlaunchd &>/dev/null &
                        exit 1
                    fi
                done

                echo "Successfully validated the correct password."

nwagner
Contributor

Thanks @daniel.hayden

That's a unique way to handle the prompt. Haven't seen it before, and I've tried to google all of em.

I tried:

launchctl asuser someusername /usr/bin/osascript -e 'display dialog [password prompt stuff here]'

To call the prompt, and gotten odd errors, sometimes. Using 'sudo -u' seems to be the most reliable so far.

I will try that 'until do' loop instead of 'while do', that may be the trick that leads me to the promise land.

Thanks for the help.