├── .gitignore
├── rescue_account
├── screenshots
│ ├── CreateRescueAccount.png
│ ├── Rescue_Password_EA.png
│ ├── RescueAccount_Cleanup.png
│ ├── adjective.txt_example.png
│ ├── Rescue_Password_Local_EA.png
│ ├── passphrase_folder_setup.png
│ ├── ChangeRescuePassword_scope.png
│ ├── JamfConnect_userswithlocalauth.png
│ ├── RescueAccount_Installed_group.png
│ ├── RescueAccount_EA_ComputerRecord.png
│ ├── RescueAccount_NeedsCleaned_group.png
│ ├── RescueAccount_passwordNeedsReset_group.png
│ ├── RescueAccount_InstalledNotEncrypted_group.png
│ └── JamfConnect_prohibitedfromnetworkaccountconnection.png
├── Rescue Password - Local - EA.sh
├── pass_phrase.sh
├── rescue_account_cleanup.sh
├── README.md
└── Rescue_Account_Password_Change.sh
├── SentinelOne
├── screenshots
│ ├── SentinelOne_Policy_Packages.png
│ ├── SentinelOne_Policy_Scripts.png
│ └── SentinelOne_registration_token.png
├── SentinelOne_Version_EA.sh
├── README.md
└── sentineone_postinstall.sh
├── README.md
├── update_safari_if_closed
├── Deprecated
├── Lastpass - Install Latest version.sh
├── BraveBrowser - Install Latest version.sh
├── Cisco Webex - install latest version.sh
├── Update_keychain_password.sh
└── pass_phrase.py
├── Add Username Spelling.sh
├── Cert Cleaner - sanitized.sh
├── Update_local_password_for_jamf_connect
├── remote_AD_user_creation.sh
├── add localadmin to filevault.sh
├── Reset GlobalProtect - Multiple Portals.sh
└── remove_and_readd_user_to_filevault
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | .DS_Store
4 | .DS_Store
5 | .DS_Store
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/rescue_account/screenshots/CreateRescueAccount.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/CreateRescueAccount.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/Rescue_Password_EA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/Rescue_Password_EA.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/RescueAccount_Cleanup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/RescueAccount_Cleanup.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/adjective.txt_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/adjective.txt_example.png
--------------------------------------------------------------------------------
/SentinelOne/screenshots/SentinelOne_Policy_Packages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/SentinelOne/screenshots/SentinelOne_Policy_Packages.png
--------------------------------------------------------------------------------
/SentinelOne/screenshots/SentinelOne_Policy_Scripts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/SentinelOne/screenshots/SentinelOne_Policy_Scripts.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/Rescue_Password_Local_EA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/Rescue_Password_Local_EA.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/passphrase_folder_setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/passphrase_folder_setup.png
--------------------------------------------------------------------------------
/SentinelOne/screenshots/SentinelOne_registration_token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/SentinelOne/screenshots/SentinelOne_registration_token.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/ChangeRescuePassword_scope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/ChangeRescuePassword_scope.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/JamfConnect_userswithlocalauth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/JamfConnect_userswithlocalauth.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/RescueAccount_Installed_group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/RescueAccount_Installed_group.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/RescueAccount_EA_ComputerRecord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/RescueAccount_EA_ComputerRecord.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/RescueAccount_NeedsCleaned_group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/RescueAccount_NeedsCleaned_group.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/RescueAccount_passwordNeedsReset_group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/RescueAccount_passwordNeedsReset_group.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/RescueAccount_InstalledNotEncrypted_group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/RescueAccount_InstalledNotEncrypted_group.png
--------------------------------------------------------------------------------
/rescue_account/screenshots/JamfConnect_prohibitedfromnetworkaccountconnection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theadamcraig/jamf-scripts/HEAD/rescue_account/screenshots/JamfConnect_prohibitedfromnetworkaccountconnection.png
--------------------------------------------------------------------------------
/SentinelOne/SentinelOne_Version_EA.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -f "/usr/local/bin/sentinelctl" ] ; then
4 | RESULT=$( /usr/local/bin/sentinelctl version | awk '{print $2 $3}' )
5 | else
6 | RESULT="not installed"
7 | fi
8 |
9 | echo "$RESULT"
--------------------------------------------------------------------------------
/rescue_account/Rescue Password - Local - EA.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | passLocation="/Library/Application Support/COMPANYNAME/rescue"
4 |
5 | if [[ -e "$passLocation" ]] ;then
6 | echo "temp file exists! Continue"
7 | result=$(cat "$passLocation")
8 | else
9 | echo "Temp file missing"
10 | result=""
11 | fi
12 |
13 | echo "$result"
14 | exit 0
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jamf-scripts
2 |
3 | These are some of the scripts that i've used in jamf.
4 | I try to notate where i'm sourcing things from other people as I did not figure all of this out from scratch.
5 | I mostly put things here when I'm using it to answer a post on JamfNation or if it's something I worked really hard on and want to put out there.
6 |
7 | I'm not the best about keeping things here up to date as I edit them and update them in my Jamf environment.
8 |
9 | Let me know if things need explanation or aren't working for you and I'll try to help.
10 |
--------------------------------------------------------------------------------
/update_safari_if_closed:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # written by theadamcraig
4 | # https://github.com/theadamcraig/jamf-scripts/blob/master/update_safari_if_closed
5 |
6 | appName="Safari Web Content"
7 |
8 | function updateSafari () {
9 | updateName=$(softwareupdate -l | grep "Safari" | grep "Label" | awk '{ print $3 }')
10 | if [ -z "$updateName" ] ; then
11 | echo "update not found."
12 | echo "listing software updates to make sure there is not an error:"
13 | echo $( softwareupdate -l )
14 | exit 1
15 | fi
16 | softwareUpdate -i "$updateName"
17 | sleep 5
18 | exit 0
19 | }
20 |
21 | appRunning=`ps aux | grep -i "$appName" | grep -v "grep"`
22 | echo "Checking if $appName is running"
23 | echo "$appRunning"
24 |
25 | if [ -z "$appRunning" ] ; then
26 | echo "update Safari"
27 | updateSafari
28 | else
29 | echo "$appName is open. skipping Update"
30 | fi
31 |
32 | exit 0
33 |
--------------------------------------------------------------------------------
/Deprecated/Lastpass - Install Latest version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ## I stopped using this. Installomator does it way better
4 |
5 | # https://github.com/Installomator/Installomator
6 |
7 | #based on a script that installs the latest version of Chrome
8 |
9 |
10 | dmgfile="LastPass.dmg"
11 | volname="LastPass Installer"
12 | logfile="/Library/Logs/LastPassInstallScript.log"
13 |
14 | url='https://lastpass.com/safariAppExtension.php?source=download'
15 |
16 |
17 | /bin/echo "--" >> ${logfile}
18 | /bin/echo "`date`: Downloading latest version." >> ${logfile}
19 | /usr/bin/curl -s -o /tmp/${dmgfile} ${url}
20 | /bin/echo "`date`: Mounting installer disk image." >> ${logfile}
21 | /usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
22 | /bin/echo "`date`: Installing..." >> ${logfile}
23 | ditto -rsrc "/Volumes/${volname}/LastPass.app" "/Applications/LastPass.app"
24 | /bin/sleep 10
25 | /bin/echo "`date`: Unmounting installer disk image." >> ${logfile}
26 | /usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
27 | /bin/sleep 10
28 | /bin/echo "`date`: Deleting disk image." >> ${logfile}
29 | /bin/rm /tmp/"${dmgfile}"
30 |
31 | exit 0
--------------------------------------------------------------------------------
/Deprecated/BraveBrowser - Install Latest version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ## I stopped using this. Installomator does it way better
4 |
5 | # https://github.com/Installomator/Installomator
6 |
7 | #based on a script that installs the latest version of Chrome
8 |
9 | dmgfile="Brave-Broswer.dmg"
10 | volname="Brave Browser"
11 | logfile="/Library/Logs/BraveInstallScript.log"
12 |
13 | url='https://brave-browser-downloads.s3.brave.com/latest/Brave-Browser.dmg'
14 |
15 |
16 | /bin/echo "--" >> ${logfile}
17 | /bin/echo "`date`: Downloading latest version." >> ${logfile}
18 | /usr/bin/curl -s -o /tmp/${dmgfile} ${url}
19 | /bin/echo "`date`: Mounting installer disk image." >> ${logfile}
20 | /usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
21 | /bin/echo "`date`: Installing..." >> ${logfile}
22 | ditto -rsrc "/Volumes/${volname}/Brave Browser.app" "/Applications/Brave Browser.app"
23 | /bin/sleep 10
24 | /bin/echo "`date`: Unmounting installer disk image." >> ${logfile}
25 | /usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
26 | /bin/sleep 10
27 | /bin/echo "`date`: Deleting disk image." >> ${logfile}
28 | /bin/rm /tmp/"${dmgfile}"
29 |
30 | exit 0
--------------------------------------------------------------------------------
/SentinelOne/README.md:
--------------------------------------------------------------------------------
1 | # SentinelOne
2 |
3 | These are instructions on how to use the SentinelOne post install script with Jamf
4 |
5 | the advantage of this workflow is that you can use one policy to install and update SentinelOne
6 |
7 | ### 1. Upload the SentinelAgent_macos_vXX_XX_X_XXXX_.pkg as downloaded from SentinelOne to Jamf
8 |
9 | ### 2. Add the script to Jamf.
10 | Change the line where it says "YOURREGISTRATIONTOKENHERE" to your sentinelone token.
11 |
12 | 
13 |
14 | ### 3. Create a new policy with whatever Scope and Trigger desired.
15 |
16 | ### 4. Add the package for SentinelOne to the policy.
17 |
18 | Make sure to set the Action to Cache
19 |
20 | 
21 |
22 | ### 5. Add the sentinelone_postinstall.sh script to the policy
23 |
24 | set the Priority to After
25 |
26 | As variable $4 add the full name of the .pkg that is being cached.
27 |
28 | 
29 |
30 | ### 6. Save the policy
31 |
32 | Caching the package puts it into /Library/Application Support/JAMF/Waiting Room/
33 |
34 | the postinstall script will look for it there and install/upgrade it with your token as needed.
35 |
36 | I've also included an Extension attribute that can be used to create smartgroups for scoping sentinel one Updates.
37 |
--------------------------------------------------------------------------------
/Deprecated/Cisco Webex - install latest version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## I stopped using this. Installomator does it way better
4 |
5 | # https://github.com/Installomator/Installomator
6 |
7 | # Based on a script that installs the latest version of chrome.
8 |
9 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
10 |
11 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
12 | echo "Failed to gather loggedInUser correctly"
13 | exit 1
14 | else
15 | echo "loggedInUser is $loggedInUser"
16 | fi
17 | loggedInUID=$(id -u "$loggedInUser")
18 |
19 |
20 | dmgfile="webexapp.dmg"
21 | volname="Cisco Webex Meetings"
22 | logfile="/Library/Logs/WebexInstallScript.log"
23 | installerName="Cisco Webex Meetings.pkg"
24 |
25 | url='https://akamaicdn.webex.com/client/webexapp.dmg'
26 |
27 |
28 | /bin/echo "--" >> ${logfile}
29 | /bin/echo "`date`: Downloading latest version." >> ${logfile}
30 | /usr/bin/curl -s -o /tmp/${dmgfile} ${url}
31 | /bin/echo "`date`: Mounting installer disk image." >> ${logfile}
32 | /usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
33 | /bin/echo "`date`: Installing..." >> ${logfile}
34 |
35 | ### run the installer as the user
36 | /bin/launchctl asuser "$loggedInUID" /usr/sbin/installer -pkg "/Volumes/${volname}/${installerName}" -target /
37 | /bin/sleep 10
38 | /bin/echo "`date`: Unmounting installer disk image." >> ${logfile}
39 | /usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
40 | /bin/sleep 10
41 | /bin/echo "`date`: Deleting disk image." >> ${logfile}
42 | /bin/rm /tmp/"${dmgfile}"
43 |
44 | exit 0
45 |
--------------------------------------------------------------------------------
/Add Username Spelling.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## We are going to add a list of words to the users dictionary so that they are not autocorrected in an annoying way.
4 | ## this will hopefully prevent the thing where Okta login windows try to autocorrect usernames
5 |
6 | # started off with script from
7 | # https://macadmins.slack.com/archives/C07MGJ2SD/p1504615636000539
8 |
9 | companyName="YourCompanyNameHere"
10 |
11 | #########################################################################################
12 | ## Get logged in user from Console.
13 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
14 |
15 | ## make sure there is a value and that it's not any of the accounts that can occasionally be a result of the console method and have an error.
16 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] || [[ "$loggedInUser" == "_mbsetupuser" ]] ; then
17 | ## if it's not a valid user let's' take the result from jamf
18 | loggedInUser="$3"
19 | fi
20 | ## convert logged in user to lowercase
21 | ## sometimes we get an mixed case user and it can create inconsistent results
22 | if [ -n "$BASH_VERSION" ]; then
23 | # assume Bash
24 | loggedInUser=$( echo "$loggedInUser" | tr [:upper:] [:lower:] )
25 | else
26 | # assume something else
27 | echo "script not written in bash, leaving as mixedcase."
28 | fi
29 | ## Make sure again that the user is valid. It's possible that $3 from Jamf is also an invalid user.
30 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] || [[ "$loggedInUser" == "_mbsetupuser" ]] ; then
31 | echo "Failed to gather loggedInUser correctly"
32 | exit 1
33 | else
34 | echo "loggedInUser is $loggedInUser"
35 | fi
36 |
37 | fullName=$( id -P $(stat -f%Su /dev/console) | awk -F '[:]' '{print $8}' )
38 |
39 | LocalDictionary="/Users/${loggedInUser}/Library/Spelling/LocalDictionary"
40 | words=("${companyName}" "$loggedInUser" "$fullName")
41 |
42 | # Backup LocalDictionary
43 | cp $LocalDictionary ${LocalDictionary}.backup
44 |
45 | # Append each word from the list
46 | for word in "${words[@]}"
47 | do
48 | echo "$word" >> $LocalDictionary
49 | done
50 |
51 | # Sort case-insensitive out to the same file
52 | sort -f $LocalDictionary -o $LocalDictionary
53 |
54 | chown -R "$loggedInUser" "$LocalDictionary"
55 | chmod -R 644 "$LocalDictionary"
56 |
57 | exit 0
--------------------------------------------------------------------------------
/SentinelOne/sentineone_postinstall.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | ## script is written by the adamcraig and sourced from https://github.com/theadamcraig/jamf-scripts/tree/master/SentinelOne
4 |
5 |
6 | # Unlike other sentinel installs this script will either Install on a new computer OR upgrade if sentinel is already installed.
7 | #I manage sentinel versions entirely from Jamf and only use one policy for both installs and upgrades.
8 |
9 | ## MAKE SURE TO ADD IN YOUR TOKEN TO REPLACE "YOURTOKENGOESHERE"
10 |
11 | ##Originally I was packaging up the SentinelAgent_macos installer and putting it in /tmp/
12 | ## then I changed to cache it so I didn't need to repackage it, so this script will check both /tmp and the waiting room.
13 |
14 | PKG_NAME="$4"
15 | ## ex: SentinelAgent_macos_v3_0_4_2657.pkg
16 | ## ex: SentinelAgent_macos_v3_2_0_2671.pkg
17 |
18 | if [ `id -u` != 0 ]; then
19 | /bin/echo "Error: You must run this command as root"
20 | exit 1
21 | fi
22 |
23 | if [[ "$PKG_NAME" == "" ]]; then
24 | /bin/echo "Error: The parameter 'SentinelOne .pkg Name' is blank. Please specify a value."
25 | exit 1
26 | fi
27 |
28 | REGISTRATION_TOKEN="/tmp/com.sentinelone.registration-token"
29 | S1_BINARY="/Library/Sentinel/sentinel-agent.bundle/Contents/MacOS/sentinelctl"
30 | WAITING_ROOM="/Library/Application Support/JAMF/Waiting Room/"
31 | INSTALL_DIRECTORY="/tmp/"
32 |
33 |
34 | ## check for the $PKG_NAME in the waiting room. if it exists then redefine INSTALL_PKG to that location
35 | ## this will allow the .pkg from sentinel one to be downloaded with out having to be repackaged
36 | ## this will also allow me to transition to doing this without needing to have 2 scripts.
37 |
38 | if [[ -e "${WAITING_ROOM}${PKG_NAME}" ]] ; then
39 | echo "Installer found in Waiting room"
40 | INSTALL_DIRECTORY="$WAITING_ROOM"
41 | fi
42 |
43 | INSTALL_PKG="${INSTALL_DIRECTORY}${PKG_NAME}"
44 | cd "${INSTALL_DIRECTORY}"
45 |
46 | echo "Install Package"
47 | echo "${INSTALL_PKG}"
48 |
49 | if [ ! -f "${INSTALL_PKG}" ]; then
50 | /bin/echo "Error: ${INSTALL_PKG} does not exist, exiting"
51 | exit 1
52 | fi
53 |
54 | ## if sentinelctl exists Upgrade sentinel one
55 | if [[ -f ${S1_BINARY} ]]; then
56 | echo "sentinel on computer. Upgrading sentinel"
57 | /usr/local/bin/sentinelctl upgrade-pkg "${INSTALL_PKG}"
58 | else
59 | #if not then install sentinel one
60 | ## create registration token
61 | cat > "${INSTALL_DIRECTORY}com.sentinelone.registration-token" << END
62 | YOURREGISTRATIONTOKENHERE
63 | END
64 | chmod -R 777 "${INSTALL_DIRECTORY}com.sentinelone.registration-token"
65 | /bin/echo "sentinel not on computer, beginning sentinel install"
66 | /usr/sbin/installer -pkg "${INSTALL_PKG}" -target /
67 |
68 | #clean up registration token
69 | sleep 10
70 | rm "${INSTALL_DIRECTORY}com.sentinelone.registration-token"
71 | fi
72 |
73 | #Clean up the installer and the jamf cache file
74 | rm -f "${INSTALL_PKG}"
75 | # also remove the cache.xml file
76 | rm -f "${INSTALL_PKG}"*
77 |
78 | exit 0
79 |
--------------------------------------------------------------------------------
/rescue_account/pass_phrase.sh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh
2 |
3 | ## written by theadamcraig https://github.com/theadamcraig/jamf-scripts/
4 | ## this script should be in the same directory as three text files
5 | ## adjectives.txt
6 | ## nouns.txt
7 | ## verbs.txt
8 |
9 | ## Optional arguments
10 | # --min Minimum characters for words default is 1
11 | # --max Maximum characters for words default is 9
12 | # --debug show full logging
13 | # --caps capitalize the first letter of every word. Default is no
14 |
15 | # Goal is to re-write pass_phrase.py in a way that will allow it to be used without python.
16 | #https://stackoverflow.com/questions/59895/how-can-i-get-the-source-directory-of-a-bash-script-from-within-the-script-itsel
17 |
18 | #hard code the default
19 |
20 | debug=0
21 | # do you want to capitalize the first letter of each word
22 | capitals="no"
23 | ## So we could easily change the divider between space - or none.
24 | div=""
25 |
26 |
27 | debugEcho() {
28 | local text="${1}"
29 | if [[ $debug = 1 ]] ; then
30 | echo "${text}"
31 | fi
32 | }
33 |
34 | ## process flags
35 | while test $# -gt 0 ; do
36 | case "${1}" in
37 | --min)
38 | shift
39 | minLength="$1"
40 | ;;
41 | --max)
42 | shift
43 | maxLength="$1"
44 | ;;
45 | --debug) debug=1
46 | ;;
47 | --caps) capitals="yes"
48 | ;;
49 | esac
50 | shift
51 | done
52 |
53 | debugEcho "DEBUG=$debug"
54 |
55 | workingDirectory=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
56 | debugEcho "Working Directory Is: ${workingDirectory}"
57 |
58 | adjList="${workingDirectory}/adjectives.txt"
59 | nounList="${workingDirectory}/nouns.txt"
60 | verbList="${workingDirectory}/verbs.txt"
61 |
62 | if [[ ! -e "${adjList}" ]] ; then
63 | debugEcho "Adjective List not found"
64 | exit 1
65 | fi
66 |
67 | if [[ ! -e "${nounList}" ]] ; then
68 | debugEcho "Noun List not found"
69 | exit 1
70 | fi
71 |
72 | if [[ ! -e "${verbList}" ]] ; then
73 | debugEcho "Verb List not found"
74 | exit 1
75 | fi
76 |
77 | debugEcho "Raw min: $minLength";
78 | debugEcho "Raw max: $maxLength";
79 |
80 | ##Make sure minLength is an int. set it to 1 if it is empty or not an int
81 | case $minLength in
82 | ''|*[!0-9]*) debugEcho bad ; minLength=1 ;;
83 | *) debugEcho good ;;
84 | esac
85 |
86 | ## makesure maxLength is an int. set it to 9 if it is empty or not an int.
87 | case $maxLength in
88 | ''|*[!0-9]*) debugEcho bad ; maxLength=9 ;;
89 | *) debugEcho good ;;
90 | esac
91 |
92 |
93 | if [[ $minLength -gt $maxLength ]] ; then
94 | debugEcho "minLength greater than maxLength. swithgin varibales"
95 | tempVar=$minLength
96 | minLength=$maxLength
97 | maxLength=$tempVar
98 | fi
99 |
100 | debugEcho "min: $minLength";
101 | debugEcho "max: $maxLength";
102 |
103 | #Let's get our words into arrays
104 | declare -a adjArray
105 | adjArray=($(cat "$adjList"))
106 | debugEcho "loaded ${#adjArray[@]} Adjectives"
107 |
108 | declare -a nounArray
109 | nounArray=($(cat "$nounList"))
110 | debugEcho "loaded ${#nounArray[@]} Nouns"
111 |
112 | declare -a verbArray
113 | verbArray=($(cat "$verbList"))
114 | debugEcho "loaded ${#verbArray[@]} Verbs"
115 |
116 | ## ELIMINATE ALL WORDS THAT DON"T FIT THE Min/max length requirements and spit the array back out
117 | checkWordLength(){
118 | local array=("$@")
119 | for i in "${!array[@]}" ; do
120 | #debugEcho "checking ${array[i]}"
121 | ## ${#string} prints the length
122 | if [[ ${#array[i]} -lt $minLength ]] || [[ ${#array[i]} -gt $maxLength ]] ; then
123 | #debugEcho "removing ${array[i]}"
124 | ##invalid length remove item
125 | unset 'array[i]'
126 | fi
127 | done
128 | #debugEcho "arrayCount= ${#array[@]}"
129 | echo "${array[@]}"
130 | }
131 |
132 | adjArray=($(checkWordLength "${adjArray[@]}"))
133 | debugEcho "refined ${#adjArray[@]} Adjectives"
134 | #declare -p adjArray
135 |
136 | nounArray=($(checkWordLength "${nounArray[@]}"))
137 | debugEcho "refined ${#nounArray[@]} Nouns"
138 |
139 | verbArray=($(checkWordLength "${verbArray[@]}"))
140 | debugEcho "refined ${#verbArray[@]} Verbs"
141 |
142 | getRandomWord(){
143 | local array=("$@")
144 | # the random range excludes the top end of the range so we don't need to subtract 1.
145 | randMax=${#array[@]}
146 | #debugEcho "maximum index is $randMax"
147 | index=$(( $RANDOM % $randMax + 0 ))
148 | #debugEcho "random index is $index"
149 | local word="${array[$index]}"
150 | # Enfocre Lower case
151 | word=$(echo $word | tr '[:upper:]' '[:lower:]')
152 | #Capitalize first letter of each word
153 | if [[ "${capitals}" == "yes" ]] ; then
154 | word="$(tr '[:lower:]' '[:upper:]' <<< ${word:0:1})${word:1}"
155 | fi
156 | echo $word
157 | }
158 |
159 | ### Example command to get a random word
160 | # word=$(getRandomWord "${adjArray[@]}")
161 |
162 | ## Now let's make a password
163 |
164 | ## adjective noun verb digit adjective noun
165 | adj1=$(getRandomWord "${adjArray[@]}")
166 | debugEcho "$adj1"
167 | noun1=$(getRandomWord "${nounArray[@]}")
168 | debugEcho "$noun1"
169 | verb=$(getRandomWord "${verbArray[@]}")
170 | debugEcho "$verb"
171 | digit=$(( $RANDOM % 10 + 0 ))
172 | debugEcho "$digit"
173 | adj2=$(getRandomWord "${adjArray[@]}")
174 | debugEcho "$adj2"
175 | noun2=$(getRandomWord "${nounArray[@]}")
176 | debugEcho "$noun2"
177 |
178 | password="${adj1}${div}${noun1}${div}${verb}${div}${digit}${div}${adj2}${div}${noun2}"
179 | echo "$password"
180 |
181 | exit 0
182 |
--------------------------------------------------------------------------------
/Cert Cleaner - sanitized.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ## Based on script found at https://macadmins.slack.com/archives/CAL8UHH1N/p1576618800010400?thread_ts=1576576382.005200&cid=CAL8UHH1N
3 |
4 | ## Update the Cert Subject on line 28 and the email address format on line 51
5 |
6 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
7 |
8 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] || [[ "$loggedInUser" == "_mbsetupuser" ]] ; then
9 | loggedInUser="$3"
10 | fi
11 |
12 | loggedInUser=$( echo "$loggedInUser" | tr [:upper:] [:lower:] )
13 |
14 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
15 | echo "Failed to gather loggedInUser correctly"
16 | exit 1
17 | else
18 | echo "loggedInUser is $loggedInUser"
19 | fi
20 | #
21 | loggedInUID=$(id -u "$loggedInUser")
22 |
23 | userKeychain="/Users/$loggedInUser/Library/Keychains/login.keychain-db"
24 |
25 | # This script will remove all instances of a system keychain cert where:
26 | # 1) The certificate subject matches the cert subject below.
27 | # 2) It does not have the latest expiration date.
28 | certSubject="YOURDOMAINHERE"
29 | #certList=$( /bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" security find-certificate -c "${certSubject}" -p -a "${userKeychain}")
30 |
31 | ## find all certs
32 | certList=$( /bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" security find-certificate -p -a "${userKeychain}")
33 |
34 | #echo "$certList"
35 |
36 | # Get each cert into an array element
37 | # Remove spaces
38 | certList=$( echo "$certList" | sed 's/ //g' )
39 | # Put a space after the end of each cert
40 | certList=$( echo "$certList" | sed 's/-----ENDCERTIFICATE-----/-----ENDCERTIFICATE----- /g' )
41 | # echo "$certList"
42 | OIFS="$IFS"
43 | IFS=' '
44 | # read -a certArray <<< "${certList}"
45 | declare -a certArray=($certList)
46 | IFS="$OIFS"
47 | i=-1
48 | dateHashList=''
49 |
50 | ## get a list of all keychain identities
51 | identityList=`/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" security find-identity -p smime -s @YOURDOMAINHHERE.com $userKeychain`
52 | ## remove all newlines
53 | identityList=${identityList//[$'\t\r\n']}
54 |
55 |
56 | declare -a deleteArray=()
57 |
58 | ## Go through the certs and add all the certs in the certArray to a deleteArray if the are in the identityList string
59 |
60 | for rawCert in "${certArray[@]}"; do
61 | let "i++"
62 | echo '--------'
63 |
64 | # Fix the begin/end certificate
65 | cert=$( echo "$rawCert" | sed 's/-----BEGINCERTIFICATE-----/-----BEGIN CERTIFICATE-----/g' )
66 | cert=$( echo "$cert" | sed 's/-----ENDCERTIFICATE-----/-----END CERTIFICATE-----/g' )
67 | certMD5=$( echo "$cert" | openssl x509 -noout -fingerprint -sha1 -inform pem | cut -d "=" -f 2 | sed 's/://g' )
68 |
69 | echo ""
70 | echo "searching identity list"
71 | echo "${identityList}"
72 | echo ""
73 |
74 | if [[ "${identityList}" == *"${certMD5}"* ]] ; then
75 | echo "Item found in identity list"
76 | echo " "
77 | else
78 | deleteArray+=( "$rawCert" )
79 | echo "adding '${certMD5}' to deleteArray"
80 | fi
81 | done
82 |
83 | echo "There are ${#certArray[@]} items in certArray"
84 | echo "There are ${#deleteArray[@]} items in deleteArray"
85 |
86 | for target in "${deleteArray[@]}"; do
87 | echo ""
88 | echo "parsing Delete Array Item"
89 | #echo "$target"
90 | for item in "${!certArray[@]}"; do
91 | if [[ ${certArray[item]} == $target ]]; then
92 | echo "item being unset from certArray"
93 | unset 'certArray[item]'
94 | fi
95 | done
96 | done
97 |
98 | echo "There are now ${#certArray[@]} items in certArray"
99 |
100 |
101 | #########################################################################################
102 | ## go through the remaining certs and
103 | i=-1
104 | # Print what we got...
105 | for cert in "${certArray[@]}"; do
106 | let "i++"
107 | echo '---------'
108 | # echo "$cert"
109 | # echo '--'
110 | # Fix the begin/end certificate
111 | cert=$( echo "$cert" | sed 's/-----BEGINCERTIFICATE-----/-----BEGIN CERTIFICATE-----/g' )
112 | cert=$( echo "$cert" | sed 's/-----ENDCERTIFICATE-----/-----END CERTIFICATE-----/g' )
113 | # echo "$cert"
114 | # echo "$cert" | openssl x509 -text
115 | certMD5=$( echo "$cert" | openssl x509 -noout -fingerprint -sha1 -inform pem | cut -d "=" -f 2 | sed 's/://g' )
116 | certDate=$( echo "$cert" | openssl x509 -text | grep 'Not After' | sed -E 's|.*Not After : ||' )
117 | certDateFormatted=`date -jf "%b %d %T %Y %Z" "${certDate}" +%Y%m%d%H%M%S`
118 | echo "Cert ${i} : ${certDate} => $certDateFormatted"
119 | echo "Cert ${i} : ${certMD5}"
120 | NL=$'\n'
121 | dateHashList="${dateHashList}${NL}${certDateFormatted} ${certMD5}"
122 | done
123 | echo
124 | dateHashList=$( echo "$dateHashList" | sort | uniq )
125 | lines=$( echo "$dateHashList" | wc -l | tr -d ' ' )
126 | let "lines--"
127 | echo "[info] There are $lines lines in the certificate date-hash list."
128 | echo
129 | i=0
130 | OIFS="$IFS"
131 | IFS=$'\n' # make newlines the only separator
132 | for dateHash in $dateHashList; do
133 | let "i++"
134 | dateNum="${dateHash%% *}"
135 | hash="${dateHash##* }"
136 | echo "${i}| Hash : \"$hash\" | dateNum : \"$dateNum\""
137 | if [[ i -ne $lines ]]; then
138 | echo "=> This cert will be removed"
139 | /bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" security delete-identity -Z $hash "${userKeychain}"
140 | echo
141 | else
142 | echo "=> This cert will not be touched because it has the latest expiration date."
143 | fi
144 | done
145 | IFS="$OIFS"
146 | exit 0
147 |
--------------------------------------------------------------------------------
/Update_local_password_for_jamf_connect:
--------------------------------------------------------------------------------
1 |
2 | adminName=$4
3 | adminPass=$5
4 | rescueUser="rescue"
5 |
6 | if [[ -z "$adminName" ]] || [[ -z "$adminPass" ]] ; then
7 | echo "admin name or admin pass missing"
8 | exit 1
9 | fi
10 |
11 | #Get logged in user
12 |
13 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
14 |
15 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
16 | echo "Failed to gather loggedInUser correctly"
17 | exit 1
18 | else
19 | echo "loggedInUser is $loggedInUser"
20 | fi
21 | loggedInUID=$(id -u "$loggedInUser")
22 |
23 | if [[ "$loggedInUser" == "$adminName" ]] ; then
24 | PROCEED="button returned:Yes"
25 | else
26 | DESCRIPTION="WARNING: This is designed to be used when the password the the primary account for this computer has been forgotten. Please use only with the assistance of Helpdesk."
27 |
28 | PROCEED="$(osascript -e 'display dialog "'"$DESCRIPTION"'" buttons {"Yes", "No"} default button "Yes"')"
29 | fi
30 |
31 | if [ "$PROCEED" = "button returned:Yes" ]; then
32 | echo "Yes, continue with script."
33 | else
34 | echo "No, cancel script."
35 | exit 1
36 | fi
37 |
38 |
39 |
40 | #### SET UP DISPLAY DIALOG FUNCTION
41 | #### SET UP DISPLAY DIALOG FUNCTION
42 | DisplayDialog(){
43 | local dialogText="$1"
44 | echo "$dialogText"
45 | #Log "Display Dialog: $dialogText"
46 | cmd="display dialog \"$dialogText\" buttons {\"Continue\"} default button 1 giving up after 180"
47 | if [[ -z "$loggedInUID" ]] || [[ -z "$loggedInUser" ]] ; then
48 | /usr/bin/osascript -e "$cmd"
49 | else
50 | /bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" /usr/bin/osascript -e "$cmd"
51 | fi
52 | }
53 |
54 |
55 | ### MAKE SURE WE HAVE BOTH VARIABLES
56 | if [[ -z "$adminName" ]] || [[ -z "$adminPass" ]] ; then
57 | DisplayDialog "Either admin username or admin password is missing"
58 | exit 1
59 | fi
60 |
61 |
62 |
63 | echo " "
64 | echo "Checking admin passsword"
65 | adminPassCheck=$(/usr/bin/dscl /Local/Default -authonly "$adminName" "$adminPass")
66 | if [[ -z "$adminPassCheck" ]]; then
67 | echo "Continue"
68 | else
69 | DisplayDialog "admin Password not set correctly"
70 | exit 1
71 | fi
72 |
73 |
74 | echo " "
75 | echo "Making sure password is eligible for Jamf Connect password reset"
76 |
77 | check4AD="$(/usr/bin/dscl localhost -list . | grep "Active Directory")"
78 |
79 | jamfConnect="/Applications/Jamf Connect.app"
80 |
81 | NETACCLIST=$(dscl . list /Users OriginalNodeName | awk '{print $1}' 2>/dev/null)
82 |
83 | if [ "${check4AD}" != "Active Directory" ]; then
84 | echo "computer not bound to AD"
85 | else
86 | DisplayDialog "Computer bound to AD"
87 | exit 1
88 | fi
89 |
90 | if [[ -e "$jamfConnect" ]] ; then
91 | echo "jamf connect installed"
92 | else
93 | DisplayDialog "Jamf connect not installed"
94 | exit 1
95 | fi
96 |
97 | if [[ -n "$NETACCLIST" ]] ; then
98 | DisplayDialog "mobile accounts found."
99 | exit 1
100 | else
101 | echo "no mobile accounts"
102 | fi
103 |
104 |
105 | #
106 | ## get username to change
107 | echo "prompting user for Account Username"
108 | userName=$(/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" /usr/bin/osascript< /dev/null )
71 |
72 | echo Token: "$token"
73 | echo Expiration: "$tokenExpiration"
74 | echo Expiration epoch: "$localTokenExpirationEpoch"
75 |
76 | setEAStatus() {
77 |
78 | echo "setting EA status"
79 |
80 | apiData="RescuePasswordStringText Field${getPass}"
81 | echo ${apiData}
82 |
83 | fullURL="$jssURL/JSSResource/computers/udid/$udid/subset/extension_attributes"
84 | echo ${fullURL}
85 |
86 | # apiPost=$(curl -s -f -u $apiUser:"$apiPass" -X "PUT" ${fullURL} -H "Content-Type: application/xml" -H "Accept: application/xml" -d "${apiData}" 2>&1 )
87 |
88 | apiPost=$( /usr/bin/curl \
89 | --header "Content-Type: text/xml" \
90 | --request PUT \
91 | --data "$apiData" \
92 | --silent \
93 | --url "$fullURL" \
94 | --header "Authorization: Bearer $token" \
95 | 2>&1 \
96 | )
97 |
98 | /bin/echo ${apiPost}
99 |
100 | #rm -f /tmp/pwlaps
101 | }
102 |
103 | uploadCheck() {
104 | echo "Checking Password"
105 | # checkPass=$(curl -s -f -u $apiUser:"$apiPass" -H "Accept: application/xml" $jssURL/JSSResource/computers/udid/$udid/subset/extension_attributes | xpath "//extension_attribute[name=$extAttName]" 2>&1 | awk -F'|' '{print $2}')
106 | checkPass=$( /usr/bin/curl \
107 | --header "Accept: text/xml" \
108 | --request GET \
109 | --silent \
110 | --url "$jssURL/JSSResource/computers/udid/"$udid"/subset/extension_attributes" \
111 | --header "Authorization: Bearer $token" | xpath "//extension_attribute[name=$extAttName]" 2>&1 | awk -F'|' '{print $2}' )
112 | echo " "
113 | # echo "full Check pass results"
114 | # echo "$checkPass"
115 | # echo " "
116 | echo " "
117 | checkPass=$(echo "$checkPass" | tr -d '\040\011\012\015')
118 | echo "$checkPass"
119 | echo "getPass"
120 | echo "$getPass"
121 |
122 | if [[ "$checkPass" == "$getPass" ]] ; then
123 | echo "password uploaded successfully"
124 | rm -f "$passLocation"
125 | else
126 | echo "password failed to upload to jamf"
127 | echo "verifying failed"
128 | alternateUploadCheck
129 | fi
130 | }
131 |
132 |
133 | alternateUploadCheck() {
134 | echo "Checking Password"
135 | # fullResult=$(curl -s -f -u $apiUser:"$apiPass" -H "Accept: application/xml" $jssURL/JSSResource/computers/udid/$udid/subset/extension_attributes)
136 |
137 | fullResult=$( /usr/bin/curl \
138 | --header "Accept: text/xml" \
139 | --request GET \
140 | --silent \
141 | --url "$jssURL/JSSResource/computers/udid/$udid/subset/extension_attributes" \
142 | --header "Authorization: Bearer $token" )
143 |
144 | trimmedResult=$( echo $fullResult | grep '$extAttNameStringfalse' )
145 | echo "$trimmedResult"
146 |
147 | newline=$'\n'
148 | trimmed2=$(echo "${trimmedResult//'$extAttName -Local'/$newline}")
149 | echo " "
150 | echo " "
151 | echo "trimmed again"
152 | echo "$trimmed2"
153 | trimmed3=$(echo "${trimmed2//'$extAttName'/'${newline}$RescuePassword'}" | grep "$extAttName" | awk -F'|' '{print $2}' )
154 | echo " "
155 | echo " "
156 | echo "trimmed again"
157 | echo "$trimmed3"
158 | echo " "
159 | echo " "
160 | checkPass=$(echo "$trimmed3" | tr -d '\040\011\012\015')
161 | echo "$checkPass"
162 | echo "getPass"
163 | echo "$getPass"
164 |
165 | if [[ "$checkPass" == "$getPass" ]] ; then
166 | echo "password uploaded successfully"
167 | rm -f "$passLocation"
168 | else
169 | echo "password failed to upload to jamf"
170 | echo "trying again."
171 | setEAStatus
172 | fi
173 |
174 | }
175 |
176 | uploadCheck
177 |
178 | # expire auth token
179 | /usr/bin/curl \
180 | --header "Authorization: Bearer $token" \
181 | --request POST \
182 | --silent \
183 | --url "$jssURL/api/v1/auth/invalidate-token"
184 |
185 | # verify auth token is valid
186 | checkToken=$( /usr/bin/curl \
187 | --header "Authorization: Bearer $token" \
188 | --silent \
189 | --url "$jssURL/api/v1/auth" \
190 | --write-out "%{http_code}" )
191 |
192 | tokenStatus=${checkToken: -3}
193 | # Token status should be 401
194 | echo "Token status: $tokenStatus"
195 |
196 | exit 0
197 |
198 |
--------------------------------------------------------------------------------
/rescue_account/README.md:
--------------------------------------------------------------------------------
1 | # Rescue Account
2 |
3 | The goal of a rescue account is to have a local account on the computer that you can give the user the password to. If a remote user forgets their password or gets locked out of their account you can give them the randomly generated rescue account password. This will allow them to get past Filevault and access the internet. Policies will run, and the user can use Self Service.
4 |
5 | Once you give a user the rescue password you can reset it just by deleting the password from jamf.
6 | 
7 |
8 | ##These are instructions on how to setup these rescue account policies with Jamf
9 |
10 | ### 1. Generate adjective.txt nount.txt & verb.txt files
11 | I'm not providing these files, there are a lot of lists you can find online, I also recommend cleaning them of NSFW words to prevent having to awkwardly tell someone a very dirty sounding password.
12 |
13 | pass_phrase.sh is expecting them to be formmatted like this:
14 |
15 | 
16 |
17 | ### 2. Install the pass_phrase.sh script and .txt files onto users computers.
18 |
19 | We put them in:
20 | >/Library/Application Support/COMPANYNAME/passphrase
21 |
22 | This filepath will need updated in a number of locations
23 |
24 | 
25 |
26 | ### 3. Setup the RescuePassword extension attribute
27 |
28 | This is a text field EA that will be updated by the API.
29 |
30 | The scripts as written are expecting this EA to be named RescuePassword without a space. if you name it something else you'll need to update the scripts accordingly.
31 |
32 | 
33 |
34 | ### 4. Setup the RescuePassword - local extension attribute
35 |
36 | Make sure to update the COMPANYNAME filepath
37 |
38 | 
39 |
40 | ### 5 Upload the Rescue_Account_Password_Change.sh and rescue_account_cleanup.sh scripts to your jamf
41 |
42 | Update the passLocation and jssURL variables in both scripts
43 |
44 | the passLocation variable is written to live next to the passphrase folder that is installed in step 2
45 |
46 | ### 6 Create The following smart groups
47 |
48 | If you choose to name your rescue account something other than rescue adjust for that here.
49 |
50 | Rescue Account Installed
51 | 
52 |
53 | Rescue Account needs cleaned
54 | 
55 |
56 | Rescue Account Password Needs Reset 
57 |
58 | Rescue Account Installed and Not Encrypted
59 | 
60 |
61 | ### 7 Create Rescue Account Policy
62 |
63 | *I have all the policies run on recurring check-in once a day*
64 | *All Policies Should to Update Inventory*
65 |
66 | Scope this to all the computers that need the Rescue Account, and have the Local Admin Account Filevault Enabled.
67 |
68 | Exclude the **Rescue Account Installed** Smart Group.
69 |
70 | - $4 is your API Username
71 | - $5 is your API Users Password
72 | - $6 is the rescue account username (Make this match the smartgroups)
73 | - $7 is your local admin username
74 | - $8 is your local admin password
75 |
76 | 
77 |
78 | This Policy will Create Rescue accounts with randomly generated password on all scoped computers.
79 | Those passwords will be uploaded to a text Extension Attribute in your jamfcloud.
80 | If the API update fails it will be saved locally and read by a separate Extension Attribute
81 |
82 | ### 8 Change Rescue Account Password Policy
83 |
84 | Clone the previous policy
85 |
86 | Change the scope to be the **Rescue Account Password Needs Reset** & **Rescue Account Installed and Not Encrypted** smart groups. Remove the **Rescue Account Installed** group from the exclusions
87 |
88 | 
89 |
90 | This Policy will Create a new rescue account password for any computer with a blank variable.
91 |
92 | This means that after you are done assisting a user with the Rescue account you can delete the text from the extension attribute variable and it will be reset.
93 |
94 | ### 9 Rescue Account Cleanup Policy
95 |
96 | Create a new policy with the Rescue_Account_Cleanup.sh script
97 |
98 | - $4 is your API Username
99 | - $5 is your API Users Password
100 |
101 | 
102 |
103 | Scope this policy to the **Rescue Account needs cleaned** smart group.
104 |
105 | This policy will get the local variable and upload it again if the password fails to upload when the account is created or the password is changed.
106 |
107 | I chose to have this local password stored because often computers that need to use the rescue account are ones where things aren't working correctly, and I didn't want a bad API call to prevent a user from being helped.
108 |
109 | ### 10 If you use Jamf Connect
110 |
111 | Make sure to account for the rescue accounted for in your jamf connect configuration
112 |
113 | If you choose to name your rescue account something other than rescue adjust for that here.
114 |
115 | add the account to Accounts Prohibited from Network Account Connection
116 | 
117 |
118 | and Users with Local Authentication Privileges
119 | 
120 |
121 |
--------------------------------------------------------------------------------
/Deprecated/Update_keychain_password.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## This script isn't really useful anymore.
4 | ## Once computers stopped being bound to AD this stopped being a problem. There are a bunch of things I could do better now if I was to still use a process like this.
5 | ## I also found that even though it made perfect sense to me, helpdesk team members never got very comfortable with this process
6 |
7 | ##UPDATE KEYCHAIN PASSWORD
8 | ## Written by adamcraig https://github.com/theadamcraig/jamf-scripts
9 | ## Last updated 4/02/2020
10 | ## Fixed Filevault Bug
11 | ## added user authentication check
12 | ## Added ability to remove and re-add user from filevault as well.
13 |
14 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
15 |
16 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
17 | echo "Failed to gather loggedInUser correctly"
18 | exit 1
19 | else
20 | echo "loggedInUser is $loggedInUser"
21 | fi
22 |
23 | loggedInUID=$(id -u "$loggedInUser")
24 |
25 | adminName=$4
26 | adminPass=$5
27 |
28 | ## Find the renamed keychains
29 | renamed=""
30 |
31 | for n in {1..9} ; do
32 | long="Users/$loggedInUser/Library/Keychains/login_renamed_$n.keychain-db"
33 | short="login_renamed_$n.keychain-db"
34 | echo "$long"
35 | if [[ ! -f $long ]] ; then
36 | echo "Above Keychain not Found"
37 | else
38 | renamed="$long"
39 | short_renamed="$short"
40 | echo "renamed set to Above Keychain"
41 | fi
42 | done
43 |
44 | ## If the rename keychain isn't found then exit
45 |
46 | if [[ -z "$renamed" ]] ; then
47 | echo "Renamed keychain not found."
48 | dialog="Old keychain not found."
49 | cmd="Tell app \"System Events\" to display dialog \"$dialog\""
50 | /usr/bin/osascript -e "$cmd"
51 | exit 1
52 | fi
53 |
54 | #renamed=`echo ${renamed%???}`
55 |
56 | echo "Prompting user for current password"
57 |
58 | ## Prompt use for current password
59 | currentPass=$(/usr/bin/osascript<> $log_file
28 |
29 | Log(){
30 | local text=$1
31 | echo "$text" >> $log_file
32 | }
33 |
34 | today=`date`
35 | Log "$today"
36 |
37 | Log "---------------------------------"
38 | Log " Checking Requirements"
39 | Log "---------------------------------"
40 |
41 | #### SET UP DISPLAY DIALOG FUNCTION
42 | DisplayDialog(){
43 | local dialogText="$1"
44 | echo "$dialogText"
45 | #echo "Display Dialog: $dialogText"
46 | cmd="display dialog \"$dialogText\" buttons {\"Continue\"} default button 1 giving up after 180"
47 | /bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" /usr/bin/osascript -e "$cmd"
48 | }
49 |
50 | ## verify that adminuser and pass variables are both passed to the user
51 | if [[ -z "$adminUser" ]] || [[ -z "$adminPass" ]] ; then
52 | DisplayDialog "either Admin User or Password is missing. Please inform Helpdesk."
53 | exit 1
54 | fi
55 |
56 | ## check the admin password
57 | adminCheck=$(/usr/bin/dscl /Local/Default -authonly "$adminUser" "$adminPass")
58 | if [[ -z "$adminCheck" ]] ; then
59 | Log "Admin password is verified"
60 | else
61 | Log "Admin Password not working"
62 | exit 1
63 | fi
64 |
65 | # If the machine is not bound to AD, then there's no purpose going any further.
66 | if [ "${check4AD}" != "Active Directory" ]; then
67 | DisplayDialog "This machine is not bound to Active Directory.\nPlease bind to AD first. "
68 | exit 1
69 | fi
70 |
71 | ## Check Filevault Status
72 | fvStatus=$(fdesetup status)
73 | if [[ "$fvStatus" == *"FileVault is On."* ]] ; then
74 | Log "Verified Filevault Enabled"
75 | else
76 | jamf policy -trigger $enableFV2JamfTrigger -forceNoRecon
77 | sleep 5
78 | DisplayDialog "Filevault Not Yet Enabled. Please Restart the computer to enable Filevault and Try again."
79 | exit 1
80 | fi
81 |
82 |
83 | ## Prompt for Username
84 | userToAdd=$(/usr/bin/osascript<
192 |
193 |
194 |
195 | Username
196 | '$adminUser'
197 | Password
198 | '$adminPass'
199 | AdditionalUsers
200 |
201 |
202 | Username
203 | '$userToAdd'
204 | Password
205 | '$userPass'
206 |
207 |
208 |
209 | ' > /tmp/fvenable.plist ### you can place this file anywhere just adjust the fdesetup line below
210 |
211 | # now enable FileVault
212 | Log "Re-adding User to Filevault"
213 | fdesetup add -i < /tmp/fvenable.plist
214 |
215 | rm -r /tmp/fvenable.plist
216 |
217 | fdeList=`fdesetup list | grep $userToAdd`
218 |
219 | if [[ "$fdeList" == *"$userToAdd"* ]] ; then
220 | DisplayDialog "$userToAdd account created.\nPhase 2 of Provisioning is complete.\nPlease select 'setup' in the menu bar by the clock and login as yourself."
221 | Log " "
222 | Log " ---------------------------------------"
223 | Log " "
224 | Log " "
225 | exit 0
226 | ## Checking password again!
227 | elif [[ ! -z "$passCheck" ]] ; then
228 | Log "Password Check Authorization failed"
229 | DisplayDialog "Automated Password Check for $userToAdd failed. \nPlease select 'setup' in the menu bar by the clock and login as yourself. \nIf you are unable to login to your account run Phase 2 again."
230 | Log " "
231 | Log " ---------------------------------------"
232 | Log " "
233 | Log " "
234 | exit 0
235 | else
236 | Log "Filevault add Failed"
237 | DisplayDialog "Automated $userToAdd account Encryption Failed. \nPlease select 'setup' in the menu bar by the clock and login as yourself."
238 | Log " "
239 | Log " ---------------------------------------"
240 | Log " "
241 | Log " "
242 | exit 0
243 | fi
244 |
--------------------------------------------------------------------------------
/add localadmin to filevault.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | ## Written by adamcraig https://github.com/theadamcraig/jamf-scripts
4 | # updated 3/31/2021
5 | # added Display Dialog function to streamline code
6 | # added secure token functionality.
7 | #
8 | # referenced this article for some of the commands
9 | #https://www.jamf.com/jamf-nation/discussions/26608/adding-user-to-filevault-using-fdesetup-and-recovery-key
10 |
11 | ## this script will prompt the user for their password and then re-add the Local Admin account to FV2
12 |
13 | ## this expects there to be a jamf policy with the trigger "install$adminName" that will be triggered if the admin account does not exist.
14 | ## also expects a trigger to enable filevault if it is not enabled.
15 |
16 | adminName=$4
17 | adminPass=$5
18 | enableFV2JamfTrigger="catalina_fv"
19 |
20 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
21 |
22 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
23 | echo "Failed to gather loggedInUser correctly"
24 | exit 1
25 | else
26 | echo "loggedInUser is $loggedInUser"
27 | fi
28 | loggedInUID=$(id -u "$loggedInUser")
29 |
30 |
31 | if [[ -z "$adminName" ]] || [[ -z "$adminPass" ]] ; then
32 | echo "adminName or adminPass variables are missing"
33 | exit 1
34 | fi
35 |
36 | adminCheck=$(id -u "$adminName")
37 | if [[ "$adminCheck" == *"no such user"* ]] || [[ -z "$adminCheck" ]] ; then
38 | echo "admin user not installed"
39 | jamf policy -trigger "install${adminName}"
40 | sleep 5
41 | fi
42 |
43 | adminCheck=$(id -u "$adminName")
44 |
45 | #### SET UP DISPLAY DIALOG FUNCTION
46 | DisplayDialog(){
47 | local dialogText="$1"
48 | echo "$dialogText"
49 | cmd="display dialog \"$dialogText\""
50 | /usr/bin/osascript -e "$cmd"
51 | }
52 |
53 | if [[ "$adminCheck" == *"no such user"* ]] || [[ -z "$adminCheck" ]] ; then
54 | echo "admin user still not installed"
55 | DisplayDialog "$adminName not installed. Exiting"
56 | exit 1
57 | fi
58 |
59 | ## Check Filevault Status
60 | fvStatus=$(fdesetup status)
61 | if [[ "$fvStatus" == *"FileVault is On."* ]] ; then
62 | echo "Verified Filevault Enabled"
63 | else
64 | jamf policy -trigger $enableFV2JamfTrigger -forceNoRecon
65 | sleep 5
66 | DisplayDialog "Filevault Not Yet Enabled. Please Restart the computer to enable Filevault and Try again."
67 | exit 1
68 | fi
69 |
70 | secureTokenUserCheck() {
71 | local userToCheck="$1"
72 | if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$userToCheck" 2>&1) =~ "ENABLED" ]]; then
73 | userToken="true"
74 | else
75 | userToken="false"
76 | fi
77 | echo "$userToken"
78 | }
79 |
80 | addSecureToken() {
81 | local tokenUser="$1"
82 | local tokenUserPass="$2"
83 | local addUser="$3"
84 | local addUserPass="$4"
85 | echo "adding token to $addUser with credentials from $tokenUser"
86 | sysadminctl -adminUser "$tokenUser" -adminPassword "$tokenUserPass" -secureTokenOn "$addUser" -password "$addUserPass"
87 | tokenAdded=$(secureTokenUserCheck "$tokenUser")
88 | echo "Token Added to $tokenUser : $tokenAdded"
89 | diskutil apfs listcryptousers /
90 | }
91 |
92 | fileVaultUserCheck() {
93 | userToCheck="$1"
94 | # this checks to see if the user exists
95 | userCheck=$(dscl . list /Users | grep "$userToCheck")
96 | if [[ -z "$userCheck" ]] ; then
97 | echo "$userToCheck does not exist"
98 | else
99 | fdeList=$(fdesetup list | grep "$userToCheck")
100 | echo "checking Filevault list $fdeList for $userToCheck"
101 | if [[ "$fdeList" == *"$userToCheck"* ]] ; then
102 | echo "FV2 Check for $userToCheck passed. Continuing"
103 | else
104 | DisplayDialog "$userToCheck not Filevault Enabled. Rectify this BEFORE you restart!"
105 | fi
106 | fi
107 | }
108 |
109 | ### CHECK SECURE TOKEN STATUS:
110 | if [[ $("/usr/sbin/diskutil" apfs listcryptousers / 2>&1) =~ "No cryptographic users" ]]; then
111 | tokenStatus="false"
112 | else
113 | tokenStatus="true"
114 | fi
115 | echo "Token Status $tokenStatus"
116 |
117 | ########### MANAGE SECURE TOKENS
118 | echo " "
119 | echo "checking secure tokens"
120 | adminToken=$(secureTokenUserCheck "$adminName")
121 | userToken=$(secureTokenUserCheck "$loggedInUser")
122 |
123 | if [[ "$tokenStatus" == "false" ]] ; then
124 | echo "No Secure Tokens, adding admin and logged in user to secure token"
125 | addSecureToken "$adminName" "$adminPass" "$loggedInUser" "$userPass"
126 | adminToken=$(secureTokenUserCheck "$adminName")
127 | userToken=$(secureTokenUserCheck "$loggedInUser")
128 | fi
129 |
130 | echo "prompting user for Account Password"
131 | userPass=$(/bin/launchctl asuser "$loggedInUID" sudo -iu "$loggedInUser" /usr/bin/osascript<&1) =~ "No cryptographic users" ]]; then
157 | tokenStatus="false"
158 | echo "Secure token still not enabled. This should never happen. Failing"
159 | exit 1
160 | else
161 | tokenStatus="true"
162 | fi
163 | ## setting this variable to false so it exists later
164 | demoteUser="false"
165 | if [[ "$adminToken" = "true" ]] ; then
166 | tokenUser="$adminName"
167 | tokenPass="$adminPass"
168 | echo "admin user has a token, setting as $tokenUser"
169 | elif [[ "$userToken" = "true" ]] ; then
170 | tokenUser="$loggedInUser"
171 | tokenPass="$userPass"
172 | echo "logged in user is only one with a token, this is bad. But will be attempted to be fixed automatically right now."
173 | ### Make sure this user is an Admin
174 | if [[ $("/usr/sbin/dseditgroup" -o checkmember -m "$loggedInUser" admin / 2>&1) =~ "yes" ]]; then
175 | echo "$loggedInUser is an admin"
176 | else
177 | echo "$loggedInUser is not an admin"
178 | demoteUser="true"
179 | ##Promoting user to admin
180 | echo "promoting user to admin"
181 | dscl . -append /groups/admin GroupMembership "$loggedInUser"
182 | fi
183 | addSecureToken "$loggedInUser" "$userPass" "$adminName" "$adminPass"
184 | ## remove admin if it was added
185 | if [[ "$demoteUser" = "true" ]] ; then
186 | echo "demoting User from admin"
187 | dscl . -delete /groups/admin GroupMembership "$loggedInUser"
188 | fi
189 | adminToken=$(secureTokenUserCheck "$adminName")
190 | if [[ "$adminToken" = "false" ]] ; then
191 | echo "adding admin user failed"
192 | exit 1
193 | else
194 | tokenUser="$adminName"
195 | tokenPass="$adminPass"
196 | fi
197 | fi
198 |
199 | adminToken=$(secureTokenUserCheck "$adminName")
200 | userToken=$(secureTokenUserCheck "$loggedInUser")
201 |
202 | if [[ "$adminToken" = "false" ]] ; then
203 | addSecureToken "$tokenUser" "$tokenPass" "$adminName" "$adminPass"
204 | fi
205 | if [[ "$userToken" = "false" ]] ; then
206 | addSecureToken "$tokenUser" "$tokenPass" "$loggedInUser" "$userPass"
207 | fi
208 |
209 | fdesetup remove -user "$adminName"
210 | #
211 | # expect -c "
212 | # spawn fdesetup add -usertoadd $adminName
213 | # expect \"Enter the primary user name:\"
214 | # send ${loggedInUser}\r
215 | # expect \"Enter the password for the user '$loggedInUser':\"
216 | # send ${userPass}\r
217 | # expect \"Enter the password for the added user '$adminName':\"
218 | # send ${adminPass}\r
219 | # expect"
220 |
221 | ## The above section errorred out due to special characters in a users password.
222 | ## found the below solution at:
223 | ## https://www.jamf.com/jamf-nation/discussions/20809/add-local-admin-to-filevault-via-script#responseChild125971
224 |
225 | # create the plist file:
226 | echo '
227 |
228 |
229 |
230 | Username
231 | '$loggedInUser'
232 | Password
233 | '$userPass'
234 | AdditionalUsers
235 |
236 |
237 | Username
238 | '$adminName'
239 | Password
240 | '$adminPass'
241 |
242 |
243 |
244 | ' > /tmp/fvenable.plist ### you can place this file anywhere just adjust the fdesetup line below
245 |
246 | # now enable FileVault
247 | fdesetup add -i < /tmp/fvenable.plist
248 |
249 | rm -r /tmp/fvenable.plist
250 |
251 | ## assign secure tokens
252 | adminToken=$(secureTokenUserCheck "$adminName")
253 |
254 | if [[ "$adminToken" = "false" ]] ; then
255 | addSecureToken "$loggedInUser" "$userPass" "$adminName" "$adminPass"
256 | fi
257 |
258 | fileVaultUserCheck "$adminName"
259 |
260 | # Make sure there is a secure token for the admin user.
261 | # Give local admin user secure token using admin user credentials established as part of Setup Assistant
262 | /usr/sbin/sysadminctl -adminUser "$loggedInUser" -adminPassword "$userPass" -secureTokenOn "$adminName" -password "$adminPass"
263 | exitresult=$(/bin/echo $?)
264 |
265 | if [ "$exitresult" = 0 ]; then
266 | /bin/echo "Successfully added secure Token to ${adminName}!"
267 | else
268 | /bin/echo "Failed to add secure Token to ${adminName}."
269 | fi
270 |
271 | diskutil apfs updatePreboot / >> /dev/null 2>&1
272 |
273 | exit "$exitresult"
--------------------------------------------------------------------------------
/Reset GlobalProtect - Multiple Portals.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #This will be the default portal.
4 | portalAddress1="${4}"
5 |
6 | portalAddress2="${5}"
7 |
8 | # For the gpUsername variable
9 | emailDomain="COMPANYNAME.COM"
10 |
11 |
12 | if [[ -z "$portalAddress1" ]] ; then
13 | echo "portal not set"
14 | exit 1
15 | fi
16 |
17 | if [[ -z "$portalAddress2" ]] ; then
18 | echo "portal not set"
19 | echo "this portal is optional, continuing"
20 | fi
21 |
22 | #########################################################################################
23 | ## Get logged in user from Console.
24 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
25 |
26 | ## make sure there is a value and that it's not any of the accounts that can occasionally be a result of the console method and have an error.
27 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] || [[ "$loggedInUser" == "_mbsetupuser" ]] ; then
28 | ## if it's not a valid user let's' take the result from jamf
29 | loggedInUser="$3"
30 | fi
31 | ## convert logged in user to lowercase
32 | ## sometimes we get an mixed case user and it can create inconsistent results
33 | if [ -n "$BASH_VERSION" ]; then
34 | # assume Bash
35 | loggedInUser=$( echo "$loggedInUser" | tr [:upper:] [:lower:] )
36 | else
37 | # assume something else
38 | echo "script not written in bash, leaving as mixedcase."
39 | fi
40 | ## Make sure again that the user is valid. It's possible that $3 from Jamf is also an invalid user.
41 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] || [[ "$loggedInUser" == "_mbsetupuser" ]] ; then
42 | echo "Failed to gather loggedInUser correctly"
43 | exit 1
44 | else
45 | echo "loggedInUser is $loggedInUser"
46 | fi
47 | ## Get the logged in UID which will be needed for some commands as well.
48 | loggedInUID=$(id -u "$loggedInUser")
49 |
50 | userHome="/Users/${loggedInUser}"
51 | # if you look in the user plist you'll see how this should be formatted.
52 | gpUsername="${loggedInUser}@${emailDomain}"
53 |
54 | userKeychain="/Users/$loggedInUser/Library/Keychains/login.keychain-db"
55 | systemKeychain="/Library/Keychains/System.keychain"
56 |
57 | ######################
58 | ## DISPLAY NOTIFICAITON lifted from installomator
59 |
60 | runAsUser() {
61 | if [[ $loggedInUser != "loginwindow" ]]; then
62 | launchctl asuser $loggedInUID sudo -u $loggedInUser "$@"
63 | fi
64 | }
65 |
66 | ## an update by acaudill to the displaynotification function to make it prefer swiftDialog if installed
67 | displaynotification() { # $1: message $2: title
68 | message=${1:-"Message"}
69 | title=${2:-"Notification"}
70 | swiftDialog="/usr/local/bin/dialog"
71 | manageaction="/Library/Application Support/JAMF/bin/Management Action.app/Contents/MacOS/Management Action"
72 | hubcli="/usr/local/bin/hubcli"
73 |
74 | if [[ -x "$swiftDialog" ]]; then
75 | runAsUser "$swiftDialog" --notification --title "$title" --message "$message"
76 | elif [[ -x "$manageaction" ]]; then
77 | "$manageaction" -message "$message" -title "$title"
78 | elif [[ -x "$hubcli" ]]; then
79 | "$hubcli" notify -t "$title" -i "$message" -c "Dismiss"
80 | else
81 | runAsUser osascript -e "display notification \"$message\" with title \"$title\""
82 | fi
83 | }
84 |
85 |
86 | ######################
87 | ## SET PORTAL FUNCTION
88 | setPortal(){
89 | local portal="${1}"
90 | if [[ -z "$portal" ]] ; then
91 | echo "Portal not provided"
92 | else
93 | echo "setting portal to $portal1"
94 | fi
95 | local plistLocation="/Library/Preferences/com.paloaltonetworks.GlobalProtect.settings.plist"
96 | rm -f "${plistLocation}"
97 |
98 | /bin/echo '
99 |
100 |
101 |
102 | Palo Alto Networks
103 |
104 | GlobalProtect
105 |
106 | PanSetup
107 |
108 | Portal
109 | '${portal}'
110 | Prelogon
111 | 0
112 |
113 |
114 |
115 |
116 | ' > "${plistLocation}"
117 |
118 | chown root:wheel "${plistLocation}"
119 | chmod 644 "${plistLocation}"
120 | }
121 | ## END SET PORTAL FUNCTION
122 | ######################
123 |
124 |
125 | setUserPortals() {
126 |
127 | local portal1="${1}"
128 | if [[ -z "$portal1" ]] ; then
129 | echo "Portal not provided"
130 | fi
131 | local portal2="${2}"
132 | if [[ -z "$portal2" ]] ; then
133 | echo "Portal not provided"
134 | fi
135 | echo "setting user portals to $portal1 $portal2"
136 | local plistLocation="${userHome}/Library/Preferences/com.paloaltonetworks.GlobalProtect.client.plist"
137 | rm -f "${plistLocation}"
138 | /bin/launchctl asuser "$loggedInUID" /usr/bin/sudo -iu "$loggedInUser" /bin/echo '
139 |
140 |
141 | PanPortalList
142 |
143 | '${portal1}'
144 | '${portal2}'
145 |
146 | User
147 | '${gpUsername}'
148 | user-credential-saved
149 | true
150 |
151 | ' > "${plistLocation}"
152 |
153 | chown "${loggedInUser}":staff "${plistLocation}"
154 | chmod 644 "${plistLocation}"
155 |
156 | }
157 |
158 | removeGPState() {
159 |
160 | echo "removing GP launchAgents and State"
161 |
162 | /bin/launchctl asuser "$loggedInUID" /usr/bin/sudo -iu "$loggedInUser" /bin/launchctl unload /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
163 | /bin/launchctl asuser "$loggedInUID" /usr/bin/sudo -iu "$loggedInUser" /bin/launchctl unload /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
164 |
165 | ## if this isn't bootout then it may be unload -w
166 | /bin/launchctl bootout system "/Library/LaunchDaemons/com.paloaltonetworks.gp.pangpsd.plist"
167 | # not running this command as user due to getting this error
168 | # Warning: Expecting a LaunchAgents path since the command was ran as user. Got LaunchDaemons instead.
169 | # `launchctl bootout` is a recommended alternative.
170 | # Unload failed: 5: Input/output error
171 |
172 | #grabbed all of this from the official GP Uninstall script
173 | echo "remove State:/Network/Service/gpd.pan/IPv4" | scutil
174 | echo "remove State:/Network/Service/gpd.pan/DNS" | scutil
175 |
176 | rm -rf "/Library/Application Support/PaloAltoNetworks/GlobalProtect"
177 | rm -rf "/Applications/GlobalProtect.app.bak"
178 | rm -rf "/System/Library/Extensions/gplock.kext"
179 | rm -rf "/Library/Extensions/gplock.kext"
180 | rm -rf "/Library/Security/SecurityAgentPlugins/gplogin.bundle"
181 |
182 | rm -rf "$userHome/Library/Application Support/PaloAltoNetworks/GlobalProtect"
183 | rm -rf "$userHome"/Library/Preferences/com.paloaltonetworks.GlobalProtect*
184 | rm -rf "$userHome"/Library/Preferences/PanGPS*
185 |
186 | security delete-generic-password -l GlobalProtect -s GlobalProtect "${userHome}/Library/Keychains/login.keychain-db"
187 |
188 | #10.9 addition to clear system preferences cache
189 | killall -SIGTERM cfprefsd
190 |
191 | killall GlobalProtect
192 | killall PanGPS
193 |
194 | echo " LaunchAgents Removed"
195 | echo " "
196 | }
197 |
198 | reloadGPAgents() {
199 |
200 | echo "reloading GP launchAgents"
201 | /bin/launchctl asuser "$loggedInUID" /usr/bin/sudo -iu "$loggedInUser" /bin/launchctl load "/Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist"
202 | /bin/launchctl asuser "$loggedInUID" /usr/bin/sudo -iu "$loggedInUser" /bin/launchctl load "/Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist"
203 | /bin/launchctl load -w "/Library/LaunchDaemons/com.paloaltonetworks.gp.pangpsd.plist"
204 | # not running this command as user due to getting an error with launchctl
205 | # Unload failed: 5: Input/output error
206 | echo "launchAgents reloaded"
207 | echo " "
208 |
209 | }
210 |
211 | ######################
212 | # DO THE THINGS
213 | #######################
214 |
215 | removeGPState
216 |
217 | setPortal "${portalAddress1}"
218 |
219 | if [[ -z "${portalAddress2}" ]] ; then
220 | echo "No second portal entered"
221 | plistLocation="${userHome}/Library/Preferences/com.paloaltonetworks.GlobalProtect.client.plist"
222 | rm -f "${plistLocation}"
223 | else
224 | setUserPortals "${portalAddress1}" "${portalAddress2}"
225 | fi
226 |
227 | killall cfprefsd
228 | killall GlobalProtect
229 | killall PanGPS
230 |
231 | echo "Load launch agents"
232 | reloadGPAgents
233 |
234 | # sleeping for 15 to allow GP time to connect
235 | sleep 15
236 |
237 | # GP check status.
238 | GPSlog="/Library/Logs/PaloAltoNetworks/GlobalProtect/PanGPS.log"
239 |
240 | loopCount=0
241 | echo " "
242 | echo "Looping GP Status check"
243 | while [ $loopCount -lt 2 ] ; do
244 | echo " "
245 | echo "loopCount=${loopCount}"
246 | ((loopCount++))
247 | ## Sleep to give the user time to do things and GP a chance to connect
248 | sleep 15
249 | ## Discovery complete is what happens with Internal when the user is on the Company network
250 | GPstatus=$( tail -r $GPSlog |grep -m 1 -o -e 'Set state to Disconnected' -e 'Set state to Connected' -e 'Set state to Discovery complete' )
251 | ## determine if GP is already connected
252 | if [[ "$GPstatus" == "Set state to Connected" ]] || [[ "$GPstatus" == "Set state to Discovery complete" ]] ; then
253 | echo "GP status is $GPstatus"
254 | break
255 | else
256 | echo "GP status is $GPstatus"
257 | fi
258 | done
259 |
260 | ## check status again
261 | if [[ "$GPstatus" == "Set state to Connected" ]] || [[ "$GPstatus" == "Set state to Discovery complete" ]] ; then
262 | echo "GP status is $GPstatus"
263 | displaynotification "Reset GlobalProtect Complete!"
264 | exit 0
265 | else
266 | echo "GP status is $GPstatus"
267 | if [[ -z "$silent" ]] ; then
268 | displaynotification "Reset GlobalProtect Complete! Sign into Okta if prompted. You may need to Refresh Connection or Restart your Computer."
269 | else
270 | echo "Script set to run silently"
271 | fi
272 | exit 0
273 | fi
--------------------------------------------------------------------------------
/remove_and_readd_user_to_filevault:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | ## written by theadamcraig and sourced from https://github.com/theadamcraig/jamf-scripts/
4 |
5 | # referenced this article for some of the commands
6 | # https://www.jamf.com/jamf-nation/discussions/26608/adding-user-to-filevault-using-fdesetup-and-recovery-key
7 |
8 | ## I hardcoded the localAdminAccount into this script to make sure it never gets removed from filevault.
9 | ## change the variable in line 20 of "localAdminAccount" to the name of your local admin
10 |
11 | # This was originally written with the expect command, but that broke when a user had a # in their password
12 | # I commented out that section, but it now uses a .plist to add the user to filevault
13 |
14 | # updated 1/20/2021 - fixed some secure token things
15 | # updated 10/30/2020 - added sections to make sure both users have a secure token and that the user being removed and re-added gets a new secure token.
16 | # updated 7/9/2020
17 |
18 | adminName=$4
19 | adminPass=$5
20 |
21 | enableFV2JamfTrigger="catalina_fv"
22 | localAdminAccount="LOCALADMINACCOUNTHERE"
23 |
24 | loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
25 |
26 | if [[ -z "$loggedInUser" ]] || [[ "$loggedInUser" == 'root' ]] || [[ "$loggedInUser" == "loginwindow" ]] ; then
27 | echo "Failed to gather loggedInUser correctly"
28 | exit 1
29 | else
30 | echo "loggedInUser is $loggedInUser"
31 | fi
32 |
33 | #### SET UP DISPLAY DIALOG FUNCTION
34 | DisplayDialog(){
35 | local dialogText="$1"
36 | echo "Display Dialog: $dialogText"
37 | cmd="Tell app \"System Events\" to display dialog \"$dialogText\""
38 | /usr/bin/osascript -e "$cmd"
39 | }
40 |
41 | ## Check Filevault Status
42 | fvStatus=$(fdesetup status)
43 | if [[ "$fvStatus" == *"FileVault is On."* ]] ; then
44 | echo "Verified Filevault Enabled"
45 | else
46 | jamf policy -trigger $enableFV2JamfTrigger -forceNoRecon
47 | sleep 5
48 | DisplayDialog "Filevault Not Yet Enabled. Please Restart the computer to enable Filevault and Try again."
49 | exit 1
50 | fi
51 |
52 |
53 | secureTokenUserCheck() {
54 | local userToCheck="$1"
55 | if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$userToCheck" 2>&1) =~ "ENABLED" ]]; then
56 | userToken="true"
57 | else
58 | userToken="false"
59 | fi
60 | echo "$userToken"
61 | }
62 |
63 | addSecureToken() {
64 | local tokenUser="$1"
65 | local tokenUserPass="$2"
66 | local addUser="$3"
67 | local addUserPass="$4"
68 | echo "adding token to $addUser with credentials from $tokenUser"
69 | sysadminctl -adminUser "$tokenUser" -adminPassword "$tokenUserPass" -secureTokenOn "$addUser" -password "$addUserPass"
70 | tokenAdded=$(secureTokenUserCheck "$addUser")
71 | echo "Token Added to $addUser : $tokenAdded"
72 | diskutil apfs listcryptousers /
73 | }
74 |
75 | fileVaultUserCheck() {
76 | userToCheck="$1"
77 | # this checks to see if the user exists
78 | userCheck=$(dscl . list /Users | grep "$userToCheck")
79 | if [[ -z "$userCheck" ]] ; then
80 | echo "$userToCheck does not exist"
81 | else
82 | fdeList=$(fdesetup list | grep "$userToCheck")
83 | echo "checking Filevault list $fdeList for $userToCheck"
84 | if [[ "$fdeList" == *"$userToCheck"* ]] ; then
85 | echo "FV2 Check for $userToCheck passed. Continuing"
86 | else
87 | DisplayDialog "$userToCheck not Filevault Enabled. Rectify this BEFORE you restart!"
88 | fi
89 | fi
90 | }
91 |
92 | ### CHECK SECURE TOKEN STATUS:
93 | if [[ $("/usr/sbin/diskutil" apfs listcryptousers / 2>&1) =~ "No cryptographic users" ]]; then
94 | tokenStatus="false"
95 | else
96 | tokenStatus="true"
97 | fi
98 | echo "Token Status $tokenStatus"
99 |
100 | ########### MANAGE SECURE TOKENS
101 | echo " "
102 | echo "checking secure tokens"
103 | adminToken=$(secureTokenUserCheck "$adminName")
104 | userToken=$(secureTokenUserCheck "$loggedInUser")
105 |
106 | if [[ "$tokenStatus" == "false" ]] ; then
107 | echo "No Secure Tokens, adding admin and logged in user to secure token"
108 | addSecureToken "$adminName" "$adminPass" "$loggedInUser" "$userPass"
109 | adminToken=$(secureTokenUserCheck "$adminName")
110 | userToken=$(secureTokenUserCheck "$loggedInUser")
111 | fi
112 |
113 |
114 | ## IF run in localAdminAccount or rescue account then prompt for which user is being updated
115 | if [[ "$loggedInUser" == "rescue" ]] || [[ "$loggedInUser" == "$localAdminAccount" ]] || [[ "$loggedInUser" == "$adminName" ]] || [[ "$loggedInUser" == "setup" ]] ; then
116 | echo "prompting user for Account Username"
117 | loggedInUser=$(/usr/bin/osascript<&1) =~ "No cryptographic users" ]]; then
169 | tokenStatus="false"
170 | echo "Secure token still not enabled. This should never happen. Failing"
171 | exit 1
172 | else
173 | tokenStatus="true"
174 | fi
175 | ## setting this variable to false so it exists later
176 | demoteUser="false"
177 | if [[ "$adminToken" = "true" ]] ; then
178 | tokenUser="$adminName"
179 | tokenPass="$adminPass"
180 | echo "admin user has a token, setting as tokenUser"
181 | elif [[ "$userToken" = "true" ]] ; then
182 | tokenUser="$loggedInUser"
183 | tokenPass="$userPass"
184 | echo "logged in user is only one with a token, this is bad. But will be attempted to be fixed automatically right now."
185 | ### Make sure this user is an Admin
186 | if [[ $("/usr/sbin/dseditgroup" -o checkmember -m "$loggedInUser" admin / 2>&1) =~ "yes" ]]; then
187 | echo "$loggedInUser is an admin"
188 | else
189 | echo "$loggedInUser is not an admin"
190 | demoteUser="true"
191 | ##Promoting user to admin
192 | echo "promoting user to admin"
193 | dscl . -append /groups/admin GroupMembership "$loggedInUser"
194 | fi
195 | addSecureToken "$loggedInUser" "$userPass" "$adminName" "$adminPass"
196 | ## remove admin if it was added
197 | if [[ "$demoteUser" = "true" ]] ; then
198 | echo "demoting User from admin"
199 | dscl . -delete /groups/admin GroupMembership "$loggedInUser"
200 | fi
201 | adminToken=$(secureTokenUserCheck "$adminName")
202 | if [[ "$adminToken" = "false" ]] ; then
203 | echo "adding admin user failed"
204 | exit 1
205 | else
206 | tokenUser="$adminName"
207 | tokenPass="$adminPass"
208 | fi
209 | fi
210 |
211 | #IF IT GETS PASSED THIS WE HAVE CONFIRMED THE CORRECT USERNAME
212 |
213 | fdesetup remove -user "$loggedInUser"
214 |
215 | # expect -c "
216 | # spawn fdesetup add -usertoadd $loggedInUser
217 | # expect \"Enter the primary user name:\"
218 | # send ${adminName}\r
219 | # expect \"Enter the password for the user '$adminName':\"
220 | # send ${adminPass}\r
221 | # expect \"Enter the password for the added user '$loggedInUser':\"
222 | # send ${userPass}\r
223 | # expect"
224 |
225 | ## The above section errorred out due to special characters in a users password.
226 | ## found the below solution at:
227 | ## https://www.jamf.com/jamf-nation/discussions/20809/add-local-admin-to-filevault-via-script#responseChild125971
228 |
229 | # create the plist file:
230 | echo '
231 |
232 |
233 |
234 | Username
235 | '$adminName'
236 | Password
237 | '$adminPass'
238 | AdditionalUsers
239 |
240 |
241 | Username
242 | '$loggedInUser'
243 | Password
244 | '$userPass'
245 |
246 |
247 |
248 | ' > /tmp/fvenable.plist ### you can place this file anywhere just adjust the fdesetup line below
249 |
250 | # now enable FileVault
251 | fdesetup add -i < /tmp/fvenable.plist
252 |
253 | rm -r /tmp/fvenable.plist
254 |
255 | adminToken=$(secureTokenUserCheck "$adminName")
256 | userToken=$(secureTokenUserCheck "$loggedInUser")
257 |
258 | if [[ "$adminToken" = "false" ]] ; then
259 | addSecureToken "$tokenUser" "$tokenPass" "$adminName" "$adminPass"
260 | fi
261 | if [[ "$userToken" = "false" ]] ; then
262 | addSecureToken "$tokenUser" "$tokenPass" "$loggedInUser" "$userPass"
263 | fi
264 |
265 |
266 | ## this verifies that the other accounts I put on the computer are still Filevault enabled.
267 | fileVaultUserCheck "$localAdminAccount"
268 | fileVaultUserCheck "rescue"
269 |
270 | fdeList=$(fdesetup list | grep "$loggedInUser")
271 |
272 | if [[ "$fdeList" == *"$loggedInUser"* ]] ; then
273 | DisplayDialog "$loggedInUser Filevault password updated successfully"
274 | exit 0
275 | else
276 | DisplayDialog "Adding $loggedInUser to FV2 Failed. Run 'Update Filevault Password' in Self Service"
277 | exit 1
278 | fi
279 |
--------------------------------------------------------------------------------
/rescue_account/Rescue_Account_Password_Change.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #################################################
4 | # RESCUE ACCOUNT SETUP
5 | #################################################
6 |
7 | ## https://github.com/theadamcraig/jamf-scripts/
8 |
9 | ## VARIABLES THAT NEED UPDATED
10 | ## Passphrase variables
11 | ## Pass location variable
12 | ## JSS URL
13 |
14 | ## updated 2/24/2021 by theadamcraig
15 | ## No longer uses python
16 | ## heavily reworked by theadamcraig 3/25/2021
17 | ## this creates the local account now so the account creation portion of the jamf policy is no longer necessary
18 | ## Initially based on script found at https://github.com/therealmacjeezy/Scripts/blob/master/LAPS%20for%20Mac/LAPSforMac.sh
19 |
20 | ########### Parameters (Required) ###############
21 | # 4 - API Username String
22 | # 5 - API Password String
23 | # 6 - Rescue Admin Username
24 | # 7 - Local Admin Username
25 | # 8 - Local Admin Password
26 |
27 | passLocation="/Library/Application Support/YOURCOMPANY/rescue"
28 | #get the filepath
29 | pathLocation=$(dirname "$passLocation")
30 | #make sure the filepath exists
31 | mkdir -p "$pathLocation"
32 | # locate the password change script
33 | passwordScript="$pathLocation/passphrase/pass_phrase.sh"
34 | # This is the new shell script version of the pass_phrase.py script.
35 |
36 | ## if the pass_phrase.sh passwordScript doesn't exist then install it
37 | if [[ ! -e "$passwordScript" ]] ; then
38 | jamf policy -trigger installpassphrase
39 | fi
40 | sleep 5
41 |
42 | # HARDCODED VALUES
43 | jssURL="https://yourdomain.jamfcloud.com"
44 | udid=$(/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/awk '/Hardware UUID:/ { print $3 }')
45 | ## extAttName also needs to be changed in the curl command in setEAStatus function.
46 | extAttName="\"RescuePassword\""
47 |
48 | ## If the passphrase script exists get the passphrase from there. Else do a random passphrase
49 | if [[ -e "$passwordScript" ]] ; then
50 | cd "$pathLocation/passphrase" || exit
51 | echo "Password Script installed."
52 | newPass=$(sh "$passwordScript" --min 3 --max 6)
53 | echo "$newPass" > "$passLocation"
54 | rescuePass=$(cat "$passLocation")
55 | else
56 | echo "Password script missing."
57 | newPass=$(env LC_CTYPE=C tr -dc "A-HJ-KM-Za-hj-km-z0-9" < /dev/urandom | head -c 24 > "$passLocation")
58 | rescuePass=$(cat "$passLocation")
59 | fi
60 |
61 | if [[ -z "$rescuePass" ]] ; then
62 | echo "failed to get password"
63 | exit 1
64 | fi
65 |
66 | # Decrypt String
67 | DecryptString() {
68 | # Usage: ~$ DecryptString "Encrypted String" "Salt" "Passphrase"
69 | echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}"
70 | }
71 |
72 | #####################################################
73 | ## VERIFY WE HAVE ALL THE VARIABLES THAT WE NEED
74 | #####################################################
75 |
76 | # Account Information
77 | if [[ -z "$4" ]]; then
78 | echo "Error: API USER MISSING"
79 | exit 1
80 | else
81 | apiUser="$4"
82 | fi
83 |
84 | if [[ -z "$5" ]]; then
85 | echo "Error: API PASS MISSING"
86 | exit 1
87 | else
88 | apiPass="$5"
89 | fi
90 |
91 | if [[ -z "$6" ]]; then
92 | echo "ERROR: RESCUE USERNAME NAME MISSING"
93 | exit 1
94 | else
95 | rescueUser="$6"
96 | fi
97 |
98 | if [[ -z "$7" ]];then
99 | echo "ERROR: LOCAL ADMIN USERNAME MISSING"
100 | exit 1
101 | else
102 | adminName="$7"
103 | fi
104 | if [[ -z "$8" ]];then
105 | echo "ERROR: LOCAL ADMIN PASSWORD MISSING"
106 | exit 1
107 | else
108 | adminPass="$8"
109 | fi
110 |
111 | ## Request Auth Token
112 | authToken=$( /usr/bin/curl \
113 | --request POST \
114 | --silent \
115 | --url "$jssURL/api/v1/auth/token" \
116 | --user "$apiUser:$apiPass" )
117 |
118 | echo "$authToken"
119 |
120 | # parse auth token
121 | token=$( /usr/bin/plutil \
122 | -extract token raw - <<< "$authToken" )
123 |
124 | tokenExpiration=$( /usr/bin/plutil \
125 | -extract expires raw - <<< "$authToken" )
126 |
127 | localTokenExpirationEpoch=$( TZ=GMT /bin/date -j \
128 | -f "%Y-%m-%dT%T" "$tokenExpiration" \
129 | +"%s" 2> /dev/null )
130 |
131 | echo Token: "$token"
132 | echo Expiration: "$tokenExpiration"
133 | echo Expiration epoch: "$localTokenExpirationEpoch"
134 |
135 |
136 | #####################################################
137 | #####################################################
138 | ## Functions that the script will use
139 | #####################################################
140 | #####################################################
141 |
142 | userExists(){
143 | local userToCheck="$1"
144 | # returns True if the user exists false if the user does not
145 | local userCheck
146 | userCheck=$(dscl . list /Users | grep "$userToCheck" | grep -v "_mbsetupuser" )
147 | if [[ "$userCheck" == "$userToCheck" ]] ; then
148 | local exists="true"
149 | else
150 | local exists="false"
151 | fi
152 | echo "$exists"
153 | }
154 |
155 | passwordCorrect() {
156 | #returns true if the user password is correct false if it is not
157 | local userName="${1}"
158 | local passWord="${2}"
159 | local passCheck
160 | passCheck=$(/usr/bin/dscl /Local/Default -authonly "$userName" "$passWord")
161 | if [[ -z "$passCheck" ]]; then
162 | local correct="true"
163 | else
164 | local correct="false"
165 | fi
166 | echo "$correct"
167 | }
168 |
169 | fileVaultUserAccess() {
170 | # returns true if the user has filevault access. false if they do not
171 | local userToCheck="$1"
172 | if [[ $(fdesetup list | grep "$userToCheck") == *"$userToCheck"* ]] ; then
173 | local fvAccess="true"
174 | else
175 | local fvAccess="false"
176 | fi
177 | echo "$fvAccess"
178 | }
179 |
180 | userIsAdmin(){
181 | local userToCheck="$1"
182 | if [[ $("/usr/sbin/dseditgroup" -o checkmember -m "$userToCheck" admin / 2>&1) =~ "yes" ]]; then
183 | local admin="true"
184 | else
185 | local admin="false"
186 | fi
187 | echo "$admin"
188 | }
189 |
190 | secureTokenUserCheck() {
191 | local userToCheck="$1"
192 | if [[ $("/usr/sbin/sysadminctl" -secureTokenStatus "$userToCheck" 2>&1) =~ "ENABLED" ]]; then
193 | userToken="true"
194 | else
195 | userToken="false"
196 | fi
197 | echo "$userToken"
198 | }
199 |
200 | addSecureToken() {
201 | local tokenUser="$1"
202 | local tokenUserPass="$2"
203 | local addUser="$3"
204 | local addUserPass="$4"
205 | echo "adding token to $addUser with credentials from $tokenUser"
206 | sysadminctl -adminUser "$tokenUser" -adminPassword "$tokenUserPass" -secureTokenOn "$addUser" -password "$addUserPass"
207 | tokenAdded=$(secureTokenUserCheck "$addUser")
208 | echo "Token Added to $addUser : $tokenAdded"
209 | diskutil apfs listcryptousers /
210 | }
211 |
212 | setEAStatus() {
213 |
214 | echo "setting EA status"
215 |
216 | apiData="RescuePasswordStringText Field${rescuePass}"
217 | echo "${apiData}"
218 |
219 | fullURL="$jssURL/JSSResource/computers/udid/$udid/subset/extension_attributes"
220 | echo "${fullURL}"
221 |
222 | # apiPost=$(curl -s -f -u "$apiUser":"$apiPass" -X "PUT" "${fullURL}" -H "Content-Type: application/xml" -H "Accept: application/xml" -d "${apiData}" 2>&1 )
223 |
224 | apiPost=$( /usr/bin/curl \
225 | --header "Content-Type: text/xml" \
226 | --request PUT \
227 | --data "$apiData" \
228 | --silent \
229 | --url "$fullURL" \
230 | --header "Authorization: Bearer $token" \
231 | 2>&1 \
232 | )
233 |
234 | /bin/echo "${apiPost}"
235 |
236 | }
237 |
238 | uploadCheck() {
239 | echo "Checking Password"
240 | # checkPass=$(curl -s -f -u "$apiUser":"$apiPass" -H "Accept: application/xml" $jssURL/JSSResource/computers/udid/"$udid"/subset/extension_attributes | xpath "//extension_attribute[name=$extAttName]" 2>&1 | awk -F'|' '{print $2}')
241 | checkPass=$( /usr/bin/curl \
242 | --header "Accept: text/xml" \
243 | --request GET \
244 | --silent \
245 | --url "$jssURL/JSSResource/computers/udid/${udid}/subset/extension_attributes" \
246 | --header "Authorization: Bearer $token" | xpath "//extension_attribute[name=$extAttName]" 2>&1 | awk -F'|' '{print $2}' )
247 | checkPass=$(echo "$checkPass" | tr -d '\040\011\012\015')
248 | echo "$checkPass"
249 | echo "rescuePass"
250 | echo "$rescuePass"
251 |
252 | if [[ "$checkPass" == "$rescuePass" ]] ; then
253 | echo "password uploaded successfully"
254 | rm -f "$passLocation"
255 | else
256 | echo "password failed to upload to jamf"
257 | echo "trying again"
258 | setEAStatus
259 | fi
260 | }
261 |
262 | createUserAccount() {
263 | local userShortName="${1}"
264 | local userFullName="${2}"
265 | local userPassword="${3}"
266 | local adminUserName="${4}"
267 | local adminUserPass="${5}"
268 | echo " "
269 | echo " "
270 | echo "Creating account using Jamf. to create home folder and suppress setup account."
271 | jamf createAccount -username "$userShortName" -realname "$userFullName" -password "$userPassword" -admin -suppressSetupAssistant
272 | sleep 1
273 | echo " "
274 | userUID=$(id -u "$userShortName")
275 | echo "getting accounts UID: $userUID"
276 | # the -keepHome option is not working
277 | # sysadminctl -deleteUser "$userToAdd" -keepHome -adminUser "$adminName" -adminPassword "$adminPass"
278 | echo " "
279 | echo "deleting account using dscl, which should leave home folder in place"
280 | dscl . delete "/Users/$userShortName"
281 | sleep 1
282 |
283 | echo " "
284 | echo "re-creating account using sysadminctl to make sure that filevault recovery mode access exists, and account has the same UID."
285 | sysadminctl -addUser "$userShortName" -fullName "$userFullName" -password "$userPassword" -adminUser "$adminUserName" -adminPassword "$adminUserPass" -home "/Users/$userShortName" -UID "$userUID" -admin
286 | sleep 1
287 | echo " "
288 | echo "Correcting owner of homefolder"
289 | chown -R "$userShortName" "/Users/$userShortName"
290 | echo " "
291 | }
292 |
293 | addUserToFilevault() {
294 | local fvUser="${1}"
295 | local fvPass="${2}"
296 | local addUser="${3}"
297 | local addPass="${4}"
298 | local remove="${5:-false}"
299 | if [[ "$remove" == "true" ]] ; then
300 | fdesetup remove -user "$addUser"
301 | fi
302 | #create the plist file:
303 | echo '
304 |
305 |
306 |
307 | Username
308 | '$fvUser'
309 | Password
310 | '$fvPass'
311 | AdditionalUsers
312 |
313 |
314 | Username
315 | '$addUser'
316 | Password
317 | '$addPass'
318 |
319 |
320 |
321 | ' > /tmp/fvenable.plist ### you can place this file anywhere just adjust the fdesetup line below
322 |
323 | # now enable FileVault
324 | fdesetup add -i < /tmp/fvenable.plist
325 |
326 | rm -r /tmp/fvenable.plist
327 | }
328 |
329 | expireApiToken() {
330 |
331 | # expire auth token
332 | /usr/bin/curl \
333 | --header "Authorization: Bearer $token" \
334 | --request POST \
335 | --silent \
336 | --url "$jssURL/api/v1/auth/invalidate-token"
337 |
338 | # verify auth token is valid
339 | checkToken=$( /usr/bin/curl \
340 | --header "Authorization: Bearer $token" \
341 | --silent \
342 | --url "$jssURL/api/v1/auth" \
343 | --write-out "%{http_code}" )
344 |
345 | tokenStatus=${checkToken: -3}
346 | # Token status should be 401
347 | echo "Token status: $tokenStatus"
348 | }
349 |
350 | #############################################
351 | #############################################
352 | ##DO THE THINGS
353 | #############################################
354 | #############################################
355 |
356 | rescueExists=$(userExists "$rescueUser" )
357 | rescueIsAdmin=$(userIsAdmin "$rescueUser" )
358 | rescuePassCorrect=$(passwordCorrect "$rescueUser" "$rescuePass")
359 | rescueFV=$(fileVaultUserAccess "$rescueUser" )
360 | rescueToken=$(secureTokenUserCheck "$rescueUser" )
361 |
362 | echo " "
363 | echo "Rescue account checks:"
364 | echo "exists: $rescueExists"
365 | echo "admin: $rescueIsAdmin (we want this to be false)"
366 | echo "passCorrect: $rescuePassCorrect"
367 | echo "filevault: $rescueFV"
368 | echo "secureToken: $rescueToken"
369 | echo " "
370 | echo " "
371 |
372 | ## FIX rescue NOT EXIST
373 | if [[ "$rescueExists" == "false" ]] ; then
374 | echo "$rescueUser does not exist. Creating account"
375 |
376 | createUserAccount "$rescueUser" "$rescueUser" "$rescuePass" "$adminName" "$adminPass"
377 | # userShortName="${1}"
378 | # userFullName="${2}"
379 | # userPassword="${3}"
380 | # adminUserName="${4}"
381 | # adminUserPass="${5}"
382 | echo "adding $rescueUser icon"
383 | jamf recon
384 | sleep 1
385 | jamf policy -trigger "${rescueUser}icon"
386 | # update other checks
387 | rescueExists=$(userExists "$rescueUser" )
388 | rescueIsAdmin=$(userIsAdmin "$rescueUser" )
389 | rescuePassCorrect=$(passwordCorrect "$rescueUser" "$rescuePass")
390 | rescueFV=$(fileVaultUserAccess "$rescueUser" )
391 | rescueToken=$(secureTokenUserCheck "$rescueUser" )
392 | if [[ "$rescueExists" == "false" ]] ; then
393 | echo "$rescueUser failed to create"
394 | exit 1
395 | fi
396 | fi
397 |
398 | ## FIX rescue IS ADMIN
399 | if [[ "$rescueIsAdmin" == "true" ]] ; then
400 | echo "$rescueUser is an admin. Demoting account"
401 | /usr/sbin/dseditgroup -o edit -n /Local/Default -d "$rescueUser" -t "user" "admin"
402 | sleep 1
403 | rescueIsAdmin=$(userIsAdmin "$rescueUser" )
404 | rescuePassCorrect=$(passwordCorrect "$rescueUser" "$rescuePass")
405 | rescueFV=$(fileVaultUserAccess "$rescueUser" )
406 | rescueToken=$(secureTokenUserCheck "$rescueUser" )
407 | if [[ "$rescueIsAdmin" == "true" ]] ; then
408 | echo "$rescueUser failed to demote"
409 | expireApiToken
410 | exit 1
411 | fi
412 | fi
413 |
414 |
415 | ## FIX rescue INCORRECT PASSWORD
416 | if [[ "$rescuePassCorrect" == "false" ]] ; then
417 | echo "$rescueUser password is incorrect. changing password"
418 | /usr/sbin/sysadminctl -adminUser "$adminName" -adminPassword "$adminPass" -resetPasswordFor "$rescueUser" -newPassword "$rescuePass"
419 | sleep 1
420 | rescuePassCorrect=$(passwordCorrect "$rescueUser" "$rescuePass")
421 | rescueFV=$(fileVaultUserAccess "$rescueUser" )
422 | rescueToken=$(secureTokenUserCheck "$rescueUser" )
423 | if [[ "$rescuePassCorrect" == "false" ]] ; then
424 | echo "$rescueUser failed to update password"
425 | expireApiToken
426 | exit 1
427 | fi
428 | fi
429 |
430 | if [[ "$rescueFV" == "false" ]] ; then
431 | echo "$rescueUser failed FV access"
432 | addUserToFilevault "$adminName" "$adminPass" "$rescueUser" "$rescuePass"
433 | sleep 1
434 | rescueFV=$(fileVaultUserAccess "$rescueUser" )
435 | rescueToken=$(secureTokenUserCheck "$rescueUser" )
436 | if [[ "$rescueFV" == "false" ]] ; then
437 | echo "$rescueUser failed to update filevault"
438 | expireApiToken
439 | exit 1
440 | fi
441 | fi
442 |
443 | if [[ "$rescueToken" == false ]] ; then
444 | echo "$rescueUser missing secure token, adding"
445 | addSecureToken "$adminName" "$adminPass" "$rescueUser" "$rescuePass"
446 | sleep 1
447 | rescueToken=$(secureTokenUserCheck "$rescueUser" )
448 | if [[ "$rescueToken" == "false" ]] ; then
449 | echo "$rescueUser failed to update secure token"
450 | expireApiToken
451 | exit 1
452 | fi
453 | fi
454 |
455 | echo " "
456 | echo "Final Checks:"
457 | echo "exists: $rescueExists"
458 | echo "admin: $rescueIsAdmin (we want this to be false)"
459 | echo "passCorrect: $rescuePassCorrect"
460 | echo "filevault: $rescueFV"
461 | echo "secureToken: $rescueToken"
462 | echo " "
463 | echo " "
464 |
465 | setEAStatus
466 | uploadCheck
467 |
468 | echo " "
469 | echo "Updating apfs preboot"
470 | diskutil apfs updatePreboot / >> /dev/null 2>&1
471 |
472 | expireApiToken
473 |
474 | exit 0
475 |
--------------------------------------------------------------------------------
/Deprecated/pass_phrase.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | ## leaving this here for reference. I no longer use this file due to Python not being included with Mac OS starting with 12.3
5 |
6 | ### I DID NOT WRITE THIS FILE! SEE LICENSE! I AM PUTTING HERE SO IT CAN BE REFERENCE TO OTHER USERS.
7 |
8 | import random
9 | import optparse
10 | import sys
11 | import re
12 | import os
13 | import math
14 | import datetime
15 | import string
16 |
17 | __LICENSE__ = """
18 | The MIT License (MIT)
19 |
20 | Copyright (c) 2012 Aaron Bassett, http://aaronbassett.com
21 |
22 | Permission is hereby granted, free of charge, to any person
23 | obtaining a copy of this software and associated documentation
24 | files (the "Software"), to deal in the Software without restriction,
25 | including without limitation the rights to use, copy, modify,
26 | merge, publish, distribute, sublicense, and/or sell copies of the
27 | Software, and to permit persons to whom the Software is furnished
28 | to do so, subject to the following conditions:
29 |
30 | The above copyright notice and this permission notice shall be
31 | included in all copies or substantial portions of the Software.
32 |
33 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
34 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
35 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
37 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
38 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
39 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 | """
41 |
42 | # random.SystemRandom() should be cryptographically secure
43 | try:
44 | rng = random.SystemRandom
45 | except AttributeError:
46 | sys.stderr.write("WARNING: System does not support cryptographically "
47 | "secure random number generator or you are using Python "
48 | "version < 2.4.\n"
49 | "Continuing with less-secure generator.\n")
50 | rng = random.Random
51 |
52 |
53 | # Python 3 compatibility
54 | if sys.version[0] == "3":
55 | raw_input = input
56 |
57 |
58 | def validate_options(options, args):
59 | """
60 | Given a set of command line options, performs various validation checks
61 | """
62 |
63 | if options.num <= 0:
64 | sys.stderr.write("Little point running the script if you "
65 | "don't generate even a single passphrase.\n")
66 | sys.exit(1)
67 |
68 | if options.max_length < options.min_length:
69 | sys.stderr.write("The maximum length of a word can not be "
70 | "lesser then minimum length.\n"
71 | "Check the specified settings.\n")
72 | sys.exit(1)
73 |
74 | if len(args) >= 1:
75 | parser.error("Too many arguments.")
76 |
77 | for word_type in ["adjectives", "nouns", "verbs"]:
78 | wordfile = getattr(options, word_type, None)
79 | if wordfile is not None:
80 | if not os.path.exists(os.path.abspath(wordfile)):
81 | sys.stderr.write("Could not open the specified {0} word file.\n".format(word_type))
82 | sys.exit(1)
83 | else:
84 | common_word_file_locations = ["{0}.txt", "~/.pass-phrase/{0}.txt"]
85 |
86 | for loc in common_word_file_locations:
87 | wordfile = loc.format(word_type)
88 | if os.path.exists(wordfile):
89 | setattr(options, word_type, wordfile)
90 | break
91 |
92 | if getattr(options, word_type, None) is None:
93 | sys.stderr.write("Could not find {0} word file, or word file does not exist.\n".format(word_type))
94 | sys.exit(1)
95 |
96 |
97 | def leet(word):
98 | geek_letters = {
99 | "a": ["4", "@"],
100 | "b": ["8",],
101 | "c": ["(",],
102 | "e": ["3",],
103 | "f": ["ph", "pH"],
104 | "g": ["9", "6"],
105 | "h": ["#",],
106 | "i": ["1", "!", "|"],
107 | "l": ["!", "|"],
108 | "o": ["0", "()"],
109 | "q": ["kw",],
110 | "s": ["5", "$"],
111 | "t": ["7",],
112 | "x": ["><",],
113 | "y": ["j",],
114 | "z": ["2",]
115 | }
116 |
117 | geek_word = ""
118 |
119 | for letter in word:
120 | l = letter.lower()
121 | if l in geek_letters:
122 | # swap out the letter *most* (80%) of the time
123 | if rng().randint(1,5) % 5 != 0:
124 | letter = rng().choice(geek_letters[l])
125 | else:
126 | # uppercase it *some* (10%) of the time
127 | if rng().randint(1,10) % 10 != 0:
128 | letter = letter.upper()
129 |
130 | geek_word += letter
131 |
132 | # if last letter is an S swap it out half the time
133 | if word[-1:].lower() == "s" and rng().randint(1,2) % 2 == 0:
134 | geek_word = geek_word[:-1] + "zz"
135 |
136 | return geek_word
137 |
138 |
139 | def mini_leet(word):
140 | geek_letters = {
141 | "a": "4",
142 | "b": "8",
143 | "e": "3",
144 | "g": "6",
145 | "i": "1",
146 | "o": "0",
147 | "s": "5",
148 | "t": "7",
149 | "z": "2",
150 | }
151 |
152 | geek_word = ""
153 |
154 | for letter in word:
155 | l = letter.lower()
156 | if l in geek_letters:
157 | letter = geek_letters[l]
158 |
159 | geek_word += letter
160 |
161 | return geek_word
162 |
163 |
164 | def generate_wordlist(wordfile=None,
165 | min_length=0,
166 | max_length=20,
167 | valid_chars='.',
168 | make_leet=False,
169 | make_mini_leet=False):
170 | """
171 | Generate a word list from either a kwarg wordfile, or a system default
172 | valid_chars is a regular expression match condition (default - all chars)
173 | """
174 |
175 | words = []
176 |
177 | regexp = re.compile("^%s{%i,%i}$" % (valid_chars, min_length, max_length))
178 |
179 | # At this point wordfile is set
180 | wordfile = os.path.expanduser(wordfile) # just to be sure
181 | wlf = open(wordfile)
182 |
183 | for line in wlf:
184 | thisword = line.strip()
185 | if regexp.match(thisword) is not None:
186 | if make_mini_leet:
187 | thisword = mini_leet(thisword)
188 | elif make_leet:
189 | thisword = leet(thisword)
190 |
191 | words.append(thisword)
192 |
193 | wlf.close()
194 |
195 | if len(words) < 1:
196 | sys.stderr.write("Could not get enough words!\n")
197 | sys.stderr.write("This could be a result of either {0} being too small,\n".format(wordfile))
198 | sys.stderr.write("or your settings too strict.\n")
199 | sys.exit(1)
200 |
201 | return words
202 |
203 |
204 | def craking_time(seconds):
205 | minute = 60
206 | hour = minute * 60
207 | day = hour * 24
208 | week = day * 7
209 |
210 | if seconds < 60:
211 | return "less than a minute"
212 | elif seconds < 60 * 5:
213 | return "less than 5 minutes"
214 | elif seconds < 60 * 10:
215 | return "less than 10 minutes"
216 | elif seconds < 60 * 60:
217 | return "less than an hour"
218 | elif seconds < 60 * 60 * 24:
219 | hours, r = divmod(seconds, 60 * 60)
220 | return "about %i hours" % hours
221 | elif seconds < 60 * 60 * 24 * 14:
222 | days, r = divmod(seconds, 60 * 60 * 24)
223 | return "about %i days" % days
224 | elif seconds < 60 * 60 * 24 * 7 * 8:
225 | weeks, r = divmod(seconds, 60 * 60 * 24 * 7)
226 | return "about %i weeks" % weeks
227 | elif seconds < 60 * 60 * 24 * 365 * 2:
228 | months, r = divmod(seconds, 60 * 60 * 24 * 7 * 4)
229 | return "about %i months" % months
230 | else:
231 | years, r = divmod(seconds, 60 * 60 * 24 * 365)
232 | return "about %i years" % years
233 |
234 |
235 | def verbose_reports(**kwargs):
236 | """
237 | Report entropy metrics based on word list size"
238 | """
239 |
240 | options = kwargs.pop("options")
241 | f = {}
242 |
243 | for word_type in ["adjectives", "nouns", "verbs"]:
244 | print("The supplied {word_type} list is located at {loc}.".format(
245 | word_type=word_type,
246 | loc=os.path.abspath(getattr(options, word_type))
247 | ))
248 |
249 | words = kwargs[word_type]
250 | f[word_type] = {}
251 | f[word_type]["length"] = len(words)
252 | f[word_type]["bits"] = math.log(f[word_type]["length"], 2)
253 |
254 | if (int(f[word_type]["bits"]) == f[word_type]["bits"]):
255 | print("Your %s word list contains %i words, or 2^%i words."
256 | % (word_type, f[word_type]["length"], f[word_type]["bits"]))
257 | else:
258 | print("Your %s word list contains %i words, or 2^%0.2f words."
259 | % (word_type, f[word_type]["length"], f[word_type]["bits"]))
260 |
261 | entropy = f["adjectives"]["bits"] +\
262 | f["nouns"]["bits"] +\
263 | f["verbs"]["bits"] +\
264 | f["adjectives"]["bits"] +\
265 | f["nouns"]["bits"]
266 |
267 | print("A passphrase from this list will have roughly "
268 | "%i (%0.2f + %0.2f + %0.2f + %0.2f + %0.2f) bits of entropy, " % (
269 | entropy,
270 | f["adjectives"]["bits"],
271 | f["nouns"]["bits"],
272 | f["verbs"]["bits"],
273 | f["adjectives"]["bits"],
274 | f["nouns"]["bits"]
275 | ))
276 |
277 | combinations = math.pow(2, int(entropy)) / 1000
278 | time_taken = craking_time(combinations)
279 |
280 | print("Estimated time to crack this passphrase (at 1,000 guesses per second): %s\n" % time_taken)
281 |
282 | def generate_passphrase(adjectives, nouns, verbs, separator):
283 | return "{0}{s}{1}{s}{2}{s}{3}{s}{4}".format(
284 | rng().choice(adjectives),
285 | rng().choice(nouns),
286 | rng().choice(verbs),
287 | rng().choice(adjectives),
288 | rng().choice(nouns),
289 | s=separator
290 | )
291 |
292 |
293 | def passphrase(adjectives, nouns, verbs, separator, num=1,
294 | uppercase=False, lowercase=False, capitalise=False):
295 | """
296 | Returns a random pass-phrase made up of
297 | adjective noun verb adjective noun
298 |
299 | I find this basic structure easier to
300 | remember than XKCD style purely random words
301 | """
302 |
303 | phrases = []
304 |
305 | for i in range(0, num):
306 | phrase = generate_passphrase(adjectives, nouns, verbs, separator)
307 | if capitalise:
308 | phrase = string.capwords(phrase)
309 | phrases.append(phrase)
310 |
311 | all_phrases = "\n".join(phrases)
312 |
313 | if uppercase:
314 | all_phrases = all_phrases.upper()
315 | elif lowercase:
316 | all_phrases = all_phrases.lower()
317 |
318 | return all_phrases
319 |
320 |
321 | if __name__ == "__main__":
322 |
323 | usage = "usage: %prog [options]"
324 | parser = optparse.OptionParser(usage)
325 |
326 | parser.add_option("--adjectives", dest="adjectives",
327 | default=None,
328 | help="List of valid adjectives for passphrase")
329 |
330 | parser.add_option("--nouns", dest="nouns",
331 | default=None,
332 | help="List of valid nouns for passphrase")
333 |
334 | parser.add_option("--verbs", dest="verbs",
335 | default=None,
336 | help="List of valid verbs for passphrase")
337 |
338 | parser.add_option("-s", "--separator", dest="separator",
339 | default=' ',
340 | help="Separator to add between words")
341 |
342 | parser.add_option("-n", "--num", dest="num",
343 | default=1, type="int",
344 | help="Number of passphrases to generate")
345 |
346 | parser.add_option("--min", dest="min_length",
347 | default=0, type="int",
348 | help="Minimum length of a valid word to use in passphrase")
349 |
350 | parser.add_option("--max", dest="max_length",
351 | default=20, type="int",
352 | help="Maximum length of a valid word to use in passphrase")
353 |
354 | parser.add_option("--valid_chars", dest="valid_chars",
355 | default='.',
356 | help="Valid chars, using regexp style (e.g. '[a-z]')")
357 |
358 | parser.add_option("-U", "--uppercase", dest="uppercase",
359 | default=False, action="store_true",
360 | help="Force passphrase into uppercase")
361 |
362 | parser.add_option("-L", "--lowercase", dest="lowercase",
363 | default=False, action="store_true",
364 | help="Force passphrase into lowercase")
365 |
366 | parser.add_option("-C", "--capitalise", "--capitalize", dest="capitalise",
367 | default=False, action="store_true",
368 | help="Force passphrase to capitalise each word")
369 |
370 | parser.add_option("--l337", dest="make_leet",
371 | default=False, action="store_true",
372 | help="7#izz R3@l|j !$ 4941Nst 7#3 w#()|e 5P|R!7 0pH t#3 7#|N6.")
373 |
374 | parser.add_option("--l337ish", dest="make_mini_leet",
375 | default=False, action="store_true",
376 | help="A l337 version which is easier to remember.")
377 |
378 | parser.add_option("-V", "--verbose", dest="verbose",
379 | default=False, action="store_true",
380 | help="Report various metrics for given options")
381 |
382 | (options, args) = parser.parse_args()
383 | validate_options(options, args)
384 |
385 | # Generate word lists
386 | adjectives = generate_wordlist(wordfile=options.adjectives,
387 | min_length=options.min_length,
388 | max_length=options.max_length,
389 | valid_chars=options.valid_chars,
390 | make_mini_leet=options.make_mini_leet,
391 | make_leet=options.make_leet)
392 |
393 | nouns = generate_wordlist(wordfile=options.nouns,
394 | min_length=options.min_length,
395 | max_length=options.max_length,
396 | valid_chars=options.valid_chars,
397 | make_mini_leet=options.make_mini_leet,
398 | make_leet=options.make_leet)
399 |
400 | verbs = generate_wordlist(wordfile=options.verbs,
401 | min_length=options.min_length,
402 | max_length=options.max_length,
403 | valid_chars=options.valid_chars,
404 | make_mini_leet=options.make_mini_leet,
405 | make_leet=options.make_leet)
406 |
407 | if options.verbose:
408 | verbose_reports(adjectives=adjectives,
409 | nouns=nouns,
410 | verbs=verbs,
411 | options=options)
412 |
413 | print(passphrase(
414 | adjectives,
415 | nouns,
416 | verbs,
417 | options.separator,
418 | num=int(options.num),
419 | uppercase=options.uppercase,
420 | lowercase=options.lowercase,
421 | capitalise=options.capitalise
422 | )
423 | )
424 |
--------------------------------------------------------------------------------