Change Password for Filevault-Enabled Management Account

etippett
Contributor II

Hi, all.

I'm wondering about recommended methods for changing the password of the local management account, which is enabled for FV2. After running a policy that changes the management account password, the stored FV2 password is not updated and I have to login with the old password. Even after logging in at the login window (which should sync the FV2 password), I still continue to have to use the old password at the FV2 pre-auth screen.

I tried running another policy to enable the management account for FV2 but this hasn't changed anything.

Thoughts?

Thanks,
Eric

1 ACCEPTED SOLUTION

etippett
Contributor II

So the final solution for changing the password on the FileVauly-enabled management account ended up being this:
1. Run the script above to change the password and ensure that FileVault is aware of the change
2. Also add Management Account > Specify New Password to the policy, with the same password as provided to the script
3. [optional] Take steps to mitigate issues with keychain password being out of sync for user

Thanks to everyone that weighed in!
Eric

View solution in original post

18 REPLIES 18

mm2270
Legendary Contributor III

I'm not 100% sure, but I think if you're changing the account password through some means other than doing it through System Preferences > Users & Groups, this is, unfortunately, normal behavior. Using a scripted or command line method to change a password doesn't seem to trigger the FDESupport process that does the password sync on the account. There may be a way to do that from a command line driven process and have it work correctly, but if so, I don't know how. You might want to peruse through Rich Trouton's (@rtrouton) derflounder blog to see if he has anything posted about this - https://derflounder.wordpress.com

On top of all this, we have a consistent problem in our environment with user account password not getting properly synced into FileVault even when the client changes their password directly on the Mac in Users & Groups.

NoahRJ
Contributor II

In our environment, changing an AD password via Users & Groups seems to update FV2, but if they change anywhere else, we have them run a policy in Self Service that first quits all open browsers/Microsoft Office applications, deletes the contents of ~/Library/Keychains and deletes ~/Library/Preferences/com.apple.keychainaccess.plist (for all users), and then reboots. The new password will almost always sync up to FV2 after this, so that might be something to try.

Alternatively, when that hasn't worked, we've gone ahead and removed the user account from FV2 and then re-added, which seems to force a password sync:

sudo fdesetup remove -user SHORTNAME

sudo fdesetup add -usertoadd SHORTNAME

bpavlov
Honored Contributor

Would ADPassMon run into issues updating the password for FV? Will probably be looking at FV later this year and just started using ADPassMon which has been working great. Just wondering if any issues would occur if the password is changed through that.

etippett
Contributor II

@mm2270 Hmmm....that's what I was afraid of, that traditional command line methods of changing the password would not trigger an update in FileVault, and Apple doesn't seem to have provided us anything with fdesetup to do so. I'm a frequent reader of DerFlounder since @rtrouton is the de facto expert on FileVault, but haven't found anything there. JAMF Support has provided me with a script that they say is a workaround but I haven't had success with it yet and continue to work with them on it. If we have success, I will post it.

@NoahRJ I thought about disabling and re-enabling as well. This gets tricky, though, since you have to provide the recovery key (ours are individual and escrowed in the JSS, so I'd have to figure out if there is a way via the API to pull the key into a script) or the username password for another enabled user. We have the username, and password of another enabled user (our HelpDesk AD login) but the password for that user is also in need of updating (password recently changed in AD) and I haven't yet figured out whether there is a way to sequence everything so that both users can be updated.

@bpavlov You may want to start another thread where you ask your specific questions about ADPassMon. I'm not using that, nor is the user I'm trying to update an AD user, so it doesn't really apply in this case.

Thanks,
Eric

mm2270
Legendary Contributor III

@etippett There's no way to pull the Recovery key via the API. (See here and vote up if you agree with it)

But you can script removing/adding accounts into FileVault using an inputplist that contains the username/password of both an existing enabled FV2 account and the one you want to remove and add back in.

etippett
Contributor II

@mm2270 Voted up! Yup, aware of the inputplist method and using it under different circumstances. Thanks for pointing it out, though!

mm2270
Legendary Contributor III

@etippett OK, so, I assume that to mean its not viable for you in this case, or at least you perceive it not to be?
You know the old password for the management account, right? And you just need to update it in FileVault? if so, its doable. I'm doing something similar for our users whose passwords get out of sync. Only difference is that since I don't know their password, I need to ask them for it (temporarily) via a cocoaDialog based script run from Self Service.

etippett
Contributor II

@mm2270 Like in my previous response, I just haven't worked through it yet, as it requires a bit of juggling. Here's the scenario:

  • We have three enabled FV users: 1) our JSS admin account, which is local to the system and is an admin, 2) a mobile AD account that is used by our HelpDesk staff, which is also an admin, and 3) the end user, which is a mobile AD account but not an admin
  • The #2 account recently had its password changed in AD, so it's locally cached password has not yet been updated on the system, at least not through any systematic method. If someone has used this account to login, it obviously therefore would have been updated. Since this means I don't know whether the password is updated or not, I am planning on using the disable/re-enable for FileVault method.
  • We are due to cycle the password on the #1 account, which is what I was trying to do and caused the birth of this thread.

I suppose there is a way for me to disable/re-enable the #2 account using the #1 current credentials, then use the #2 account to disable/re-enable the #1 account using the #2 updated credentials. Do you know if reboots would be required in between some of these steps?

Thanks!
Eric

mm2270
Legendary Contributor III

Hi @etippett I currently have a script/policy users can run from Self Service to "fix" their FileVault password sync problem. While I'm not going to specifically post the script here, I can outline how it works, and maybe this will spark a way you can do the same thing, but completely automated. Since our users are actually the only account enabled for FileVault typically, and we have no idea what their AD passwords are, the script uses cocoaDialog and asks them for input along the way to do what it does. Here's the basic outline.

  1. User runs policy from SS, downloads a script
  2. Script first checks to make sure the logged in user is enabled for FileVault
  3. Script checks to make sure the account is Active Directory based (requires communication with domain controllers at this point)
  4. If GUID from AD matches the GUID in FileVault it continues a) If script can't communicate with AD, it stops and alerts user that either they are not connected to the network or their Mac has fallen out of domain trust b) Assuming it passes initial checks, it asks user for their OLD FileVault password, since this is what we need to make any changes to FV2
  5. Script creates a new 'tempfv2' account on the Mac and uses the Mac's serial # as the account password
  6. Script creates a plist file with the users account & password for authorization, and includes the new tempfv2 account + password to add that account into FileVault.
  7. Script calls fdesetup and uses the plist file to 'enable' tempfv2 for FileVault.
  8. If successful, script deletes the plist file, then asks user for their NEW password (the one FileVault won't currently take) a) Script asks them to enter it twice for confirmation (need to make sure they enter it correct) and compares the 2 entries to make sure they match.
  9. Assuming a match, script first removes the current user from the FV2 authorization list. (fdesetup remove -user username)
  10. Assuming it successfully removed the user (checks for this), script then creates another plist in /tmp/ that includes the username & their correct password as the user to add, but uses the tempfv2 account we created before for the FileVault authorization.
  11. Script uses the plist to add the user back in, but with their new, current password.
  12. Assuming successful, it now has 2 accounts in the FV2 auth list, but removes the tempfv2 account from FileVault and deletes the tempfv2 account from the Mac.
  13. Script cleans up by deleting the plist file created in step 10.

Hopefully the above steps will help you find a way to do the same for your management account. I'm pretty certain this can be done in a similar way.

etippett
Contributor II

@mm2270 That's quite clever and I'll keep it in mind as a possible solution. Since I have more options available to me with FV-enabled users, I'll probably try to find a method which doesn't require composing such an impressive script! ;) Right now that is:

  1. Disable the #2 account for FV
  2. Use the JAMF-provided script to update the password for the #1 account (note that this does NOT update the management account password in the JSS, so I've also added a Management Account > Specify New Password portion to the policy as well)
  3. Re-enable the #2 account using the updated credentials for #1

Here is the script from JAMF (note that it uses the base64 command to obfuscate the password for the account; you may need to modify this for your situation):

#!/bin/bash

##### HEADER BEGINS #####
#
# syncFileVaultPassword.sh
# 
# Created:  2014-12-30
# Modified: 2015-01-05
#
# 
#
# Priority: Before
# Category: User Accounts - No SS
# Info: This script will set the password for a specified account in such a way that FileVault will also be updated with this new password.
# Notes: Does not require knowledge of previous password.
#
# Requires the following parameters be set.
#     ! setUsername: $4
#     ! setPassword: $5
#
##### HEADER ENDS #####

# Set the mountpoint passed by Casper.
mountPoint="${1}"

# Set the computername passed by Casper.
computerName="${2}"

# Set the username passed by Casper.
username="${3}"

if [ -n "${4}" ]; then
    setUsername="${4}"
else
    echo 'This script requires a username.'
    exit 1
fi

if [ -n "${5}" ]; then
    setPassword="$(base64 --decode <<< "${5}")"
else
    echo 'This script requires a password.'
    exit 1
fi

# First, make sure that this is a FileVault enabled user. Otherwise, don't bother.
if [ "$(fdesetup list | grep -ic "^${setUsername},")" -eq '0' ]; then
    exit 1
fi

# Then, set the password as root. And set it again as the user to update FileVault.
expect 1>/dev/null <<ENDEXPECT
    set timeout -1
    spawn dscl . passwd "/Users/${setUsername}"
    expect "*:"
    send -- {$setPassword}
    send -- "
"
    spawn sudo -iu "${setUsername}" dscl . passwd "/Users/${setUsername}"
    expect "*:"
    send -- {$setPassword}
    send -- "
"
    expect "*:"
    send -- {$setPassword}
    send -- "
"
ENDEXPECT

Thanks,
Eric

nessts
Valued Contributor II

@etippett did you try to just run

dscl . -passwd users/$setUsername "$setPassword"

expect scripts are neat and all but it seems like one more thing to break.

etippett
Contributor II

@nessts Yes, that was the first thing I tried. It does not update the FV password for the user it is run against :( Even worse it seems to prevent the login window from properly updating the FV password after the fact.

I agree, I'm not huge on the expect script, but it's what is working for now. Apple haven't provided us a documented way of doing this, and JAMF has an open defect on it, so it is what it is unfortunately.

nessts
Valued Contributor II

cool, thank you for confirming.

etippett
Contributor II

So the final solution for changing the password on the FileVauly-enabled management account ended up being this:
1. Run the script above to change the password and ensure that FileVault is aware of the change
2. Also add Management Account > Specify New Password to the policy, with the same password as provided to the script
3. [optional] Take steps to mitigate issues with keychain password being out of sync for user

Thanks to everyone that weighed in!
Eric

danny_friedman_
New Contributor III

So I just ran into this issue, and it is quite the issue. The solution above will not work in my environment.

@etippett - This solution will not work if the current (or future) admin password contains a dollar sign, due to how expect works. This will also be an issue for any environments that wish to use JAMF's script to send FileVault 2 keys (learned that the hard way, fortunately, this can be resolved using -inputlist).

The only solutions that I was able to come up with to resolve this issue were:

  1. Creating a script that writes a local file that contains a list of computer names with their associated FileVault 2 individual keys (this would be extremely tedious, as I'd have to get these keys/computer names manually from the JSS web GUI). The base script would then remove the user requiring the password change from the list of FileVault 2 users, change that user's password using dscl . passwd "/Users/<username>", then add the user back as a FileVault 2 user. When prompted for credentials of a FileVault 2 user to add the user back as a FileVault 2 user, the script would then use grep to find the line containing the current target computer's name and associated FileVault 2 individual key, assign the key to a variable, and then input it as the password to authenticate the add to FileVault 2 users. using the file containing keys to can then parse to use the keys as authentication. expect can be used here if the new password does not contain a '$'.

  2. The other option is manually changing all passwords via SSH, which I would hope I don't have to do.

As the first solution I came up with will be a very labor-intensive script to create and test, does anyone else have a better idea? I pretty diappointed that there isn't a better solution than using an expect statement in a script to resolve this.

krispayne
Contributor

I just spent a long time debugging the expect in the above script. The password expansions were including the braces so the expect statement should actually read as (at least in my environment of 10.12.6):

expect 1>/dev/null <<ENDEXPECT
    set timeout -1
    spawn dscl . passwd "/Users/${setUsername}"
    expect "*:"
    send -- "$setPassword"
    send -- "
"
    spawn sudo -iu "${setUsername}" dscl . passwd "/Users/${setUsername}"
    expect "*:"
    send -- "$setPassword"
    send -- "
"
    expect "*:"
    send -- "$setPassword"
    send -- "
"
ENDEXPECT

Also note to future me:

echo "password to encode" | base64

Then put that in the password field ($5) in your policy.

robmorton
Contributor

I have been trying to get this script to work and so far have had no success. As I am not that great at debugging expect items, I figured I would throw my items here and see if anyone has thoughts. Basically the first command (spawn dscl . passwd "/Users/${setUsername}") is never really working. I have trimmed down the expect area to only that command and what to send.

expect -d 1>/dev/null <<ENDEXPECT
    exp_internal -f /Users/robadmin/Desktop/expect.log 1
    set timeout -1
    spawn dscl . passwd "/Users/${setUsername}"
    expect "*:"
    send -- $setPassword
    send -- "
"
##    spawn sudo -iu "${setUsername}" dscl . passwd "/Users/${setUsername}"
#    spawn dscl -u $setUsername -p . -passwd "/Users/$setUsername"
#    expect "*:"
#    send -- $setPassword
#    send -- "
"
#    expect "*:"
#    send -- $setPassword
#    send -- "
"
ENDEXPECT

I am creating a log file now to troubleshoot. That file produces...
spawn dscl . passwd /Users/superadmin
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {3476}

expect: does "" (spawn_id exp7) match glob pattern ":"? no
New Password: expect: does "New Password: " (spawn_id exp7) match glob pattern "
:"? yes
expect: set expect_out(0,string) "New Password:"
expect: set expect_out(spawn_id) "exp7"
expect: set expect_out(buffer) "New Password:"
send: sending "Password3" to { exp7 }
send: sending " " to { exp7 }

To me, that means that superadmin should now have a password of Password3. It does not get that password. It will remain the old password. If I copy and paste the command in the terminal
dscl . passwd /Users/superadmin
It will ask for the New Password: and I can enter Password3 and that will work.

Does anyone have any idea what I am doing incorrectly here? I am running all commands as root.

pitcherj
New Contributor III

Hello,

This is perhaps too little, too late at this point but I ran into the same issue myself.

In my case, I'm implementing the local password updater/escrow tool located here:
https://github.com/jamf/JSS_Scripts

The problem is, when you update the local account password you've got an orphaned (old) password still set for that account in the pre-boot partition of that macOS computer.

Additionally, the password change portion of that script doesn't work anymore as of one of the more recent updates to 10.14.

#!/bin/sh
###################################################################################################
#
# Copyright (c) 2015, JAMF Software, LLC.  All rights reserved.
#
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are met:
#               * Redistributions of source code must retain the above copyright
#                 notice, this list of conditions and the following disclaimer.
#               * Redistributions in binary form must reproduce the above copyright
#                 notice, this list of conditions and the following disclaimer in the
#                 documentation and/or other materials provided with the distribution.
#               * Neither the name of the JAMF Software, LLC nor the
#                 names of its contributors may be used to endorse or promote products
#                 derived from this software without specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY
#       EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#       WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#       DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY
#       DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#       (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#       LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#       ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
###################################################################################################
#
# DESCRIPTION
#
#   This script is designed for a custom workflow to update local account passwords securely.
#
#   Recommended usage:
#        - Specify User Variables in JSS Parameters
#        - Set Script to Run on a timed basis
#        - Set Update Inventory to run as part of policy
#
####################################################################################################
#
# HISTORY
#
#   - Created by Bram Cohen, JAMF Software, LLC, on September 30, 2015
#   - Updated by Bram Cohen, JAMF Software, LLC, on October 1, 2015
#        - Fixed an issue where delimiters were present after decoding hash 
#        - Fixed an issue where one could visually discern part of the string
#        - Fixed an issue where the salt may cause a decode failure
#        - Added additional notation and made the output pretty
#
####################################################################################################

###################################################################################################
# User Variable Section - Specify below or leave blank to fill in from the JSS in the next section
###################################################################################################

#Specify the user whose password will be updated
targetedUsername="useraccount"

#Specify the length of password we would like to generate
lengthOfPassword="32"

#Specify a salt value to throw into the hash. THIS MUST BE SPECIFIED IN THE E.A. AS WELL
salt="myreallycoolsalt"

#Specify where you would like the hash stored on the machine
configurationFile="/Library/Preferences/com.jamfsoftware.management.plist"

###################################################################################################
# JSS Variable Section - If above are null and we have values from the JSS, it will them fill in
###################################################################################################
if [ "$4" != "" ] && [ "$targetedUsername" == "" ]; then
    targetedUsername="$4"
fi

if [ "$5" != "" ] && [ "$lengthOfPassword" == "" ]; then
    lengthOfPassword="$5"
fi

if [ "$6" != "" ] && [ "$salt" == "" ]; then
    salt="$6"
fi

if [ "$7" != "" ] && [ "$configurationFile" == "" ]; then
    configurationFile="$7"
fi


####################################################################################################
# Functions Section -  DO NOT MODIFY BELOW THIS LINE
####################################################################################################

###############################
# Function problemDetector
###############################
problemDetector() {
    if [[ "$problem" == "1" ]]; then
        echo "==================================================
 A problem was detected in this script run:
"
        echo "$problemDescription"
        exit 1
    fi

}

###############################
# Function populateVariables
###############################
populateVariables() {
    binaryLocation=$(/usr/bin/type -a jamf | awk '{print $NF}')
    machineUUID=$(/usr/sbin/system_profiler SPHardwareDataType | grep UUID | awk '{print $NF}' | base64)

    if [ "$binaryLocation" == "" ]; then
        problem=1
        problemString=" - Unable to locate the jamf binary."
        problemDescription=$(echo "$problemDescription
$problemString")
    fi
    if [ "$machineUUID" == "" ]; then
        problem=1
        problemString=" - Unable to locate locate the UUID of the computer."
        problemDescription=$(echo "$problemDescription
$problemString")
    fi

}

###############################
# Function verifyUser
###############################
verifyUser() {
    userCheck=$(/usr/bin/dscl . -list /Users | grep "${targetedUsername}" | wc -l | awk '{print $NF}')

    if [ "$userCheck" == "0" ]; then
        problem=1
        problemString=" - Unable to locate the username $targetedUsername on this computer"
        problemDescription=$(echo "$problemDescription
$problemString")
    fi  

}

###############################
# Function checkForConfig
###############################
checkForConfig() {
echo "Checking for Configuration File"
if [ ! -f "$configurationFile" ]; then
    echo " - Configuration not found, creating one"
    /usr/bin/touch $configurationFile
    /usr/sbin/chown root:wheel $configurationFile
else
    echo " - Configuration Found
"
fi

}

###############################
# Function generatePassword
###############################
generatePassword() {

    echo "Generating Password"
    hundredDigits=$(/usr/bin/openssl rand -base64 100)
    passwordString=$(echo "${hundredDigits}" | tr -cd '[[:alnum:]]._-' | cut -c1-${lengthOfPassword})

}

###############################
# Function obfuscatePassword
###############################
obfuscatePassword() {

    echo " - Salting and Hashing password"
    saltPassword=$(echo "${machineUUID}${passwordString}${salt}" | base64)

}

###############################
# Function writePasswordToConfig
###############################
writePasswordToConfig() {
    echo " - Writing string to file
"
    /usr/bin/defaults write $configurationFile string $saltPassword

    echo "Confirming hash was written in a readable manner"
    verifyHash=$(/usr/bin/defaults read $configurationFile string)

    if [ "$saltPassword" == "$verifyHash" ]; then
        echo " - Hash readability confirmed
"
    else
        problem=1
        problemString=" - Failed to read the hash in the configuration file."
        problemDescription=$(echo "$problemDescription
$problemString")
    fi

}

###############################
# Function changePassword
###############################
changePassword() {

    echo "Changing password and updating FileVault password for user ${targetedUsername}."

expect -c "
log_user 0
spawn dscl . passwd "/Users/${targetedUsername}"
expect "*:"
send "${passwordString}"
expect "*:"
send "YOUROLDBADPASSWORDHERE"
spawn sudo -iu "${targetedUsername}" dscl . passwd "/Users/${targetedUsername}"
expect "*:"
send "${passwordString}"
expect "*:"
send "YOUROLDBADPASSWORDHERE"
log_user 1
expect eof
"

}

###############################
# Function cleanUp
###############################

cleanUp() {

    variablesInUse="targetedUsername
    lengthOfPassword
    salt
    configurationFile
    problem
    problemString
    problemDescription
    binaryLocation
    machineUUID
    userCheck
    hundredDigits
    passwordString
    saltPassword
    verifyHash
    debugFlag"


    echo "Unsetting Variables
"

    for vars in $variablesInUse; do
        unset $vars
    done

}

####################################################################################################
# Function Execution Section -  SERIOUSLY, DO NOT MODIFY BELOW THIS LINE UNLESS YOU KNOW WHATS UP!
####################################################################################################
echo "
==========================================
  Executing password randomization Script 
=========================================="

populateVariables
problemDetector
verifyUser
problemDetector
checkForConfig
generatePassword
obfuscatePassword
writePasswordToConfig
problemDetector
changePassword
cleanUp


echo "==========================================
=========================================="
exit 0

The key(s) to all of this working is (1) using dscl to change the password and (2) supplying the OLD password instead of the new password twice in the sudo -iu dscl command (probably a timing issue, perhaps a sleep would resolve this as well):

expect -c "
log_user 0
spawn dscl . passwd "/Users/${targetedUsername}"
expect "*:"
send "${passwordString}"
expect "*:"
send "YOUROLDBADPASSWORDHERE"
spawn sudo -iu "${targetedUsername}" dscl . passwd "/Users/${targetedUsername}"
expect "*:"
send "${passwordString}"
expect "*:"
send "YOUROLDBADPASSWORDHERE"
log_user 1
expect eof
"

Hope this helps someone!

Jacob