Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use user's authentication to create the service account #134

Open
arilewis93 opened this issue Sep 26, 2023 · 10 comments
Open

Use user's authentication to create the service account #134

arilewis93 opened this issue Sep 26, 2023 · 10 comments
Labels
enhancement New feature or request
Milestone

Comments

@arilewis93
Copy link

arilewis93 commented Sep 26, 2023

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

@Macjutsu
Copy link
Owner

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.

@Macjutsu Macjutsu added the enhancement New feature or request label Sep 26, 2023
@Macjutsu Macjutsu added this to the v4.0 milestone Sep 26, 2023
@arilewis93
Copy link
Author

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....

@Macjutsu
Copy link
Owner

Full disclosure... I don't ever plan to look at the v3 code again.

@arilewis93
Copy link
Author

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!

@Macjutsu Macjutsu modified the milestones: v4.0.0, v4.1.0 Oct 23, 2023
@arilewis93
Copy link
Author

arilewis93 commented Nov 1, 2023

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.

@Macjutsu
Copy link
Owner

Macjutsu commented Nov 1, 2023

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.

@arilewis93
Copy link
Author

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)

@Macjutsu
Copy link
Owner

Macjutsu commented Nov 2, 2023

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.

@arilewis93
Copy link
Author

arilewis93 commented Nov 3, 2023

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!

@Macjutsu
Copy link
Owner

Macjutsu commented Nov 3, 2023

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.

@Macjutsu Macjutsu modified the milestones: v5.0.0, v5.x.x Jul 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants