super
super copied to clipboard
Use user's authentication to create the service account
When the user is asked for their password to install an update, use the user account to create a service account.
When the user clicks "install" and is prompted for their password (if their is no super service account and MDM fails), use those credentials to create a super service account so that that are never asked again.
This would require inserting the following code before line 7494:
adminACCOUNT=$currentUserNAME
adminPASSWORD="$dialogRESULT"
# if the user is not an admin, make them an admin and store the demote var to user later.
[[ $(dseditgroup -o checkmember -m "$currentUserNAME" admin) != "yes $currentUserNAME is a member of admin" ]] && demote=true && sudo /usr/sbin/dseditgroup -o edit -a $currentUserNAME -t user admin
# Create service account now
manageUpdateCredentials
# Demote user
[[ $demote ]] && sudo /usr/sbin/dseditgroup -o edit -d $currentUserNAME -t user admin
That code is based on v3 so it won't work in v4.
There is also a lot of other ancillary preference management code that needs to be added for a new feature. Further this will require the creation of a new function for the service account creation because there would be two different workflows using similar code.
In other words, while this is a good idea that I plan to implement, it's more complicated than just the changes you have here.
Cool. I'm testing it on v3 in our fleet of 800 or so macs and so far it seems to be working...
A though I had (which I'm gonna mention even though this enhancement will make it irrelevant) is a way to run the service account creation workflow without everything else. I made a script to ask the user for their password and pass it into super
and then check if the account was created and then tell them it was successful with a popup. The issue is that super
get "sidetracked" and the pop up takes forever to show because super
is busy checking for updates....
Full disclosure... I don't ever plan to look at the v3 code again.
Cool, I hear that. So I guess I'll use as I have it, and my comment above is clear enough (I think?) for others to also use if they wish. But at least the idea will be taken forward to v4. BTW, thanks for such a fantastic tool! Whenever I look at the dashboard after an update, I can't believe how fast the compliance rises - I never knew being so annoying could be so affective!
I added the following in version 4.0.0 after line 7983 (for reference, it was after this line if [[ "${dialog_user_auth_valid}" == "TRUE" ]]; then
- in case you want to use it on a newer version)
auth_service_add_via_admin_account_option="${current_user_account_name}"
auth_service_add_via_admin_password_option="${auth_local_password}"
[[ $(dseditgroup -o checkmember -m "$auth_service_add_via_admin_account_option" admin) != "yes $auth_service_add_via_admin_account_option is a member of admin" ]] && demote=true && sudo /usr/sbin/dseditgroup -o edit -a $auth_service_add_via_admin_account_option -t user admin
# Create service account now
manage_authentication_options
[[ $demote ]] && sudo /usr/sbin/dseditgroup -o edit -d $auth_service_add_via_admin_account_option -t user admin
sudo super --workflow-install-now
This worked whether the user was an admin or not. The last line sudo super --workflow-install-now
causes a loop if for some reason creation of the account failed. so you may want to leave it out.
Yikes! It's not safe to put that function there.
I'm happy that this works for you... and I do plan to implement this officially... but it will take quite a bit more changes to support this feature in a safe way.
I'm interested why you say its not safe - maybe using this code with other implementation options (like trying to use MDM auth or a local existing account) wont work as expected, but using it without any of those and just a plist to define deferrals, I cant see the issue (yes I have gone through the code, every line in the func)
That code was designed to run during the startup workflow, and where you have it is now much later in the workflow. Again, it might work... but it was never designed for or tested to run that late in the workflow.
To implement this properly (as I plan to do in a future version) there is a lot more work involved to implement this as a configurable option, test the crap out of it, and document.
manage_authentication_create_service_account () {
log_super "Status: Creating and validating new super service account..."
# Validate that ${auth_service_add_via_admin_account_option} exists, is a volume owner, a local admin, and that ${auth_service_add_via_admin_password_option} is correct.
if [[ "${auth_legacy_service_migrate}" != "TRUE" ]]; then # If migrating a legacy sevice account, then the ${auth_service_add_via_admin_account_option} doesn't have to be validated.
if [[ $(groups "${auth_service_add_via_admin_account_option}" 2> /dev/null | grep -c 'admin') -eq 0 ]]; then
# User is not an admin, elevate them to create the service account.
demote=true
/usr/sbin/dseditgroup -o edit -a $auth_service_add_via_admin_account_option -t user admin
fi
if [[ "${auth_error_new}" != "TRUE" ]]; then
auth_local_account="${auth_service_add_via_admin_account_option}"
auth_local_password="${auth_service_add_via_admin_password_option}"
check_auth_local_account
unset auth_local_account
unset auth_local_password
[[ "${auth_error_local}" == "TRUE" ]] && auth_error_new="TRUE"
fi
fi
# Set the ${auth_service_account}, ${auth_service_real_name}, and ${auth_service_password} in preparation to create the super service account.
if [[ "${auth_error_new}" != "TRUE" ]]; then
if [[ "${auth_legacy_service_migrate}" != "TRUE" ]]; then # If migrating a legacy sevice account, then a new service account doesn't have to be created.
local auth_service_account
local auth_service_real_name
if [[ -n "${auth_service_account_option}" ]]; then
auth_service_account="${auth_service_account_option}"
auth_service_real_name="${auth_service_account_option}"
else
auth_service_account="super"
auth_service_real_name="Super Update Service"
fi
local auth_service_password
if [[ -n "${auth_service_password_option}" ]]; then
auth_service_password="${auth_service_password_option}"
else
auth_service_password=$(uuidgen)
fi
fi
# Save ${auth_service_account} and ${auth_service_password} credentials to keychain and then validate retrieval by setting ${auth_service_account_keychain} and ${auth_service_password_keychain}.
security add-generic-password -a "super_auth_service_account" -s "Super Update Service" -w "$(echo "${auth_service_account}" | base64)" -T "/usr/bin/security" "/Library/Keychains/System.keychain" > /dev/null 2>&1
auth_service_account_keychain=$(security find-generic-password -w -a "super_auth_service_account" "/Library/Keychains/System.keychain" 2> /dev/null | base64 --decode)
if [[ "${auth_service_account}" != "${auth_service_account_keychain}" ]]; then
[[ "${verbose_mode_option}" == "TRUE" ]] && log_echo "Verbose Mode: Function ${FUNCNAME[0]}: auth_service_account is: ${auth_service_account}"
[[ "${verbose_mode_option}" == "TRUE" ]] && log_echo "Verbose Mode: Function ${FUNCNAME[0]}: auth_service_account_keychain is: ${auth_service_account_keychain}"
log_super "Auth Error: Unable to validate keychain item for the super service account, deleting keychain item."; auth_error_new="TRUE"
security delete-generic-password -a "super_auth_service_account" "/Library/Keychains/System.keychain" > /dev/null 2>&1
create_service_account_failed=true
fi
security add-generic-password -a "super_auth_service_password" -s "Super Update Service" -w "$(echo "${auth_service_password}" | base64)" -T "/usr/bin/security" "/Library/Keychains/System.keychain" > /dev/null 2>&1
auth_service_password_keychain=$(security find-generic-password -w -a "super_auth_service_password" "/Library/Keychains/System.keychain" 2> /dev/null | base64 --decode)
if [[ "${auth_service_password}" != "${auth_service_password_keychain}" ]]; then
[[ "${verbose_mode_option}" == "TRUE" ]] && log_echo "Verbose Mode: Function ${FUNCNAME[0]}: auth_service_password is: ${auth_service_password}"
[[ "${verbose_mode_option}" == "TRUE" ]] && log_echo "Verbose Mode: Function ${FUNCNAME[0]}: auth_service_password_keychain is: ${auth_service_password_keychain}"
log_super "Auth Error: Unable to validate keychain item for the super service password, deleting keychain item."; auth_error_new="TRUE"
security delete-generic-password -a "super_auth_service_password" "/Library/Keychains/System.keychain" > /dev/null 2>&1
create_service_account_failed=true
fi
fi
# If the saved credentials are valid then create the new super service account.
if [[ "${auth_error_new}" != "TRUE" ]] && [[ "${auth_legacy_service_migrate}" != "TRUE" ]]; then # If migrating a legacy sevice account, then a new service account doesn't have to be created.
local auth_service_uid
auth_service_uid=501
while [[ $(id "${auth_service_uid}" 2>&1 | grep -c 'no such user') -eq 0 ]]; do
auth_service_uid=$((auth_service_uid + 1))
done
local sysadminctl_response
sysadminctl_response=$(sysadminctl -addUser "${auth_service_account}" -fullName "${auth_service_real_name}" -password "${auth_service_password}" -UID "${auth_service_uid}" -GID 20 -shell "/dev/null" -home "/dev/null" -picture "${DISPLAY_ICON_FILE_CACHE}" -adminUser "${auth_service_add_via_admin_account_option}" -adminPassword "${auth_service_add_via_admin_password_option}" 2>&1)
[[ "${verbose_mode_option}" == "TRUE" ]] && log_super "Verbose Mode: Function ${FUNCNAME[0]}: sysadminctl_response is:\n${sysadminctl_response}"
dscl . create /Users/"${auth_service_account}" IsHidden 1
fi
# Validate the super service account locally.
auth_local_account="${auth_service_account}"
auth_local_password="${auth_service_password}"
check_auth_local_account
unset auth_local_account
unset auth_local_password
[[ "${auth_error_local}" == "TRUE" ]] && auth_error_new="TRUE"
# If the super service account is valid then update ${SUPER_LOCAL_PLIST}.
if [[ "${auth_error_new}" != "TRUE" ]]; then
[[ "${auth_legacy_service_migrate}" != "TRUE" ]] && log_super "Status: Created new super serivce account."
[[ "${auth_legacy_service_migrate}" == "TRUE" ]] && log_super "Status: Validated migrated super serivce account."
defaults write "${SUPER_LOCAL_PLIST}" AuthServiceAccount -bool true
auth_service_account_saved="TRUE"
else
[[ "${auth_legacy_service_migrate}" != "TRUE" ]] && log_super "Auth Error: Unable to validate newly created super service account, deleting account"; auth_error_new="TRUE"
[[ "${auth_legacy_service_migrate}" == "TRUE" ]] && log_super "Auth Error: Unable to validate migrated super service account, deleting account."; auth_error_new="TRUE"
sysadminctl -deleteUser "${auth_service_account}" > /dev/null 2>&1
security delete-generic-password -a "super_auth_service_account" "/Library/Keychains/System.keychain" > /dev/null 2>&1
security delete-generic-password -a "super_auth_service_password" "/Library/Keychains/System.keychain" > /dev/null 2>&1
auth_service_account_saved="FALSE"
unset auth_service_account_keychain
unset auth_service_password_keychain
create_service_account_failed=true
fi
# If the user was not an admin initially, remove admin access that was granted to create the service account.
[[ $demote ]] && sudo /usr/sbin/dseditgroup -o edit -d $auth_service_add_via_admin_account_option -t user admin
}
I made this as a standalone function - not sure what you want to call it or where you want to insert it.
It means that after this line: if [[ "${dialog_user_auth_valid}" == "TRUE" ]]; then
you need these 5 lines of code:
if [[ -z "${create_service_account_failed}" ]]; then
auth_service_add_via_admin_account_option="${current_user_account_name}"
auth_service_add_via_admin_password_option="${auth_local_password}"
manage_authentication_create_service_account; super --workflow-install-now
fi
(obviously the last line depends on what you want to call the new func, and how you want to make it configurable by making an if
before it)... Hope this helps others!
That's a better approach.... and similar to the one I will take when I have time to implement this in v4.1... other priorities first.