Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
887 lines (617 sloc)
23.4 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# mOSL/Lockdown | |
# Lockdown | |
# Lockdown macOS Catalina security settings | |
declare -r LOCKDOWN_VERSION="v3.2.1" | |
set -uo pipefail | |
# -u prevent using undefined variables | |
# -o pipefail force pipelines to fail on first non-zero status code | |
IFS=$'\n\t' | |
# Set Internal Field Separator to newlines and tabs | |
# This makes bash consider newlines and tabs as separating words | |
# See: http://redsymbol.net/articles/unofficial-bash-strict-mode/ | |
### Define Colours ### | |
/usr/bin/tput sgr0; | |
# reset colors | |
readonly RED="$(/usr/bin/tput setaf 1)" | |
readonly RESET="$(/usr/bin/tput sgr0)" | |
readonly BOLD="$(/usr/bin/tput bold)" | |
### END Colours ### | |
function usage { | |
echo -e "\\n Audit or Fix macOS security settings🔒🍎\\n" | |
echo -e " Usage: ./Lockdown [list | audit {setting} | fix {setting} | version | debug]\\n" | |
echo " list - List settings that can be audited/ fixed" | |
echo " audit - Audit the status of all or chosen setting" | |
echo -e " ${RED}fix${RESET} - Attempt to fix all or chosen setting\\n" | |
echo " ${RED}fix-force${RESET} - Same as 'fix' however bypasses user confirmation prompt" | |
echo -e " (Can be used to invoke Lockdown from other scripts)\\n" | |
echo " version - Print Lockdown version string" | |
echo -e " debug - Print debug info for troubleshooting\\n" | |
exit 0 | |
} | |
### UTILITY FUNCTIONS ### | |
# macos_compatability_check | |
# audit | |
# fix | |
# mode_check | |
# get_fix_mode_permission | |
# follow_symlink | |
# verify_signature | |
# check_index | |
# sudo_prompt | |
# ctrl_c | |
# check_if_vm | |
# check_sip | |
# debug | |
# full_disk_access_check | |
# version | |
function macos_compatability_check { | |
# Check if running on a Mac | |
# Check if the Mac is running the supoorted version of macOS | |
local -r supported_macos_version="10.15" | |
local os | |
local current_macos_version | |
os="$(/usr/bin/uname -s)" | |
if [[ "${os}" != "Darwin" ]]; then | |
echo "[❌] Lockdown was built for macOS" | |
exit 1 | |
fi | |
current_macos_version="$(/usr/bin/sw_vers -productVersion | /usr/bin/awk -F '.' '{print $1 "." $2}')"; | |
if [ "${current_macos_version}" != "${supported_macos_version}" ]; then | |
echo "[❌] Lockdown was built for macOS ${supported_macos_version}.x" | |
echo "[🍺] This is macOS ${current_macos_version}" | |
exit 1 | |
fi | |
} | |
function audit { | |
local title=${1:?No title passed} | |
local command=${2:?No command passed} | |
if bash -c "${command}"; then | |
echo " [✅] ${title}" | |
return 0 | |
else | |
echo " [❌] ${title}" | |
return 1 | |
fi | |
} | |
function fix { | |
local title=${1:?No title passed} | |
local command=${2:?No command passed} | |
if [[ "${command}" == "null" ]]; then | |
echo " [⚠️ ] ${BOLD}Can't fix${RESET}: ${title}" | |
return 1 | |
fi | |
if bash -c "${command}"; then | |
echo " [✅] ${BOLD}FIXED${RESET}: ${title}" | |
if [[ "${title}" == "Check SIP enabled" ]]; then | |
echo " [⚠️ ] ${BOLD}Reboot required for SIP configuration changes to take effect" | |
fi | |
return 0 | |
else | |
echo " [❌] ${BOLD}Failed to fix${RESET}: ${title}" | |
return 1 | |
fi | |
} | |
function mode_check { | |
local mode=${1:?} | |
local title=${2:?} | |
local audit_command=${3:?} | |
local fix_command=${4:-"null"} | |
if [[ "${mode}" == "audit" ]]; then | |
audit "${title}" "${audit_command}" | |
elif [[ "${mode}" == "fix" ]]; then | |
fix "${title}" "${fix_command}" | |
fi | |
} | |
function get_fix_mode_permission { | |
# Double check with user before making changes to their system | |
local fix_mode_permission="" | |
echo "[⚠️ ] You are about to engage ${BOLD}${RED}FIX${RESET} mode which ${BOLD}${RED}WILL${RESET} make changes to your Mac 💻" | |
echo -n "[⚠️ ] Do you want to continue? (y/${BOLD}N${RESET}) " | |
read -r fix_mode_permission | |
if [[ "${fix_mode_permission}" =~ ^(y|Y)$ ]]; then | |
echo "[✅] ${USER} has chosen to continue" | |
sudo_prompt | |
return 0 | |
else | |
echo "[❌] ${USER} has chosen to quit!" | |
exit 1 | |
fi | |
} | |
function follow_symlink { | |
# If Lockdown is symlinked in $PATH | |
# Follow the symlink(s) to the real location of Lockdown | |
# This is required for Minisign to verify the signature | |
base=$(/usr/bin/basename "${0}") | |
# Lockdown | |
source="${BASH_SOURCE[0]}" | |
# /x/y/z/Lockdown | |
while [ -h "${source}" ]; do | |
# While $source is a symlink | |
dir="$(cd -P "$(/usr/bin/dirname "${source}")" && pwd)" | |
# Get directory of $source | |
# cd into dir of $source and run `pwd` | |
source="$(/usr/bin/readlink "${source}")" | |
# get target of $source symlink | |
if [[ "${source}" != /* ]]; then | |
source="${dir}/${source}" | |
fi | |
done | |
parent=$(/usr/bin/dirname "${source}") | |
# get directory of $source | |
lockdown_path="$(cd -P "${parent}" && pwd)/${base}" | |
# Build path to Lockdown | |
} | |
function verify_signature { | |
# Verify Lockdown signature with minisign | |
local lockdown_version_path | |
lockdown_version_path=$(echo "${LOCKDOWN_VERSION}" | tr -d 'v') | |
follow_symlink | |
if [ -x "/usr/local/bin/minisign" ]; then | |
if [[ "${lockdown_path}" =~ "Cellar/mosl" ]]; then | |
# If installed by Brew | |
if ! /usr/local/bin/minisign -x "/usr/local/Cellar/mosl/${lockdown_version_path}/Lockdown.minisig"\ | |
-Vm "${lockdown_path}" -P "RWTiYbJbLl7q6uQ70l1XCvGExizUgEBNDPH0m/1yMimcsfgh542+RDPU" >/dev/null 2>&1; then | |
echo "[❌] Failed to validate Lockdown signature with minisign" | |
exit 1 | |
fi | |
else | |
if ! /usr/local/bin/minisign -Vm "${lockdown_path}" -P "RWTiYbJbLl7q6uQ70l1XCvGExizUgEBNDPH0m/1yMimcsfgh542+RDPU" >/dev/null 2>&1; then | |
echo "[❌] Failed to validate Lockdown signature with minisign" | |
exit 1 | |
fi | |
fi | |
else | |
echo "[⚠️ ] ${BOLD}Unable to verify Lockdown signature${RESET} as minisign is not installed" | |
echo -e "[🍺] brew install minisign\\n" | |
is_codesigned="no minisgn" | |
return 1 | |
fi | |
} | |
function check_index { | |
# Check that index supplied to audit/ fix isn't greater than the number of entries in settings | |
local setting_num=${1:-0} | |
local -i max_setting_num | |
max_setting_num=$(( ${#settings[@]} - 1 )) | |
if ! [[ "${setting_num}" =~ ^-?[0-9]+$ ]] ; then | |
echo -e "\\n [❌] ${setting_num} ${RED}is not an integer!${RESET}" | |
echo " [⚠️ ] Pick a setting between 1 and ${max_setting_num}" | |
exit 1 | |
fi | |
if [ "${setting_num}" -gt "${max_setting_num}" ]; then | |
echo -e "\\n [❌] ${RED}No setting with index of ${setting_num}${RESET}" | |
echo " [⚠️ ] Pick a setting between 1 and ${max_setting_num}" | |
exit 1 | |
fi | |
} | |
function sudo_prompt { | |
sudo --prompt="[⚠️ ] Password required to run some commands with 'sudo': " -v | |
# Aquire sudo privlidges now so we can show a custom prompt | |
# -v updates the user's cached credentials, does not run a command | |
} | |
function ctrl_c { | |
echo -e "\\n[❌] ${USER} has chosen to quit!" | |
exit 1 | |
} | |
function check_if_vm { | |
if system_profiler SPHardwareDataType | grep -q "VMware"; then | |
# TODO: Only detects VMware. Add VirtualBox, Parallels, generic? | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
function check_sip { | |
if csrutil status | grep -q "enabled"; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
function debug { | |
local is_admin="False" | |
local has_full_disk_access="False" | |
if /usr/bin/groups | /usr/bin/grep -q 'admin'; then | |
is_admin="True" | |
fi | |
if full_disk_access_check; then | |
has_full_disk_access="True" | |
fi | |
if check_if_vm; then | |
is_vm="True" | |
fi | |
if verify_signature >/dev/null 2>&1; then | |
is_codesigned="True" | |
# If the signature has been broken and you uncomment the | |
# call to verify_signature in main and try to run | |
# ./Lockdown debug this verify_signature call will still | |
# exit 1 with no explanation | |
fi | |
if check_sip; then | |
sip="True" | |
fi | |
echo -e "${RED}Debug information:${RESET} \\n" | |
echo " mOSL Version: ${LOCKDOWN_VERSION}" | |
/usr/sbin/system_profiler SPSoftwareDataType | /usr/bin/grep 'Version' | |
echo " Is admin: ${is_admin}" | |
echo " Full Disk Access: ${has_full_disk_access}" | |
echo " Virtual Machine: ${is_vm}" | |
echo " Codesigned: ${is_codesigned}" | |
# Codesigned will only ever be "True" or "no minisign" as | |
# verify_signature is the first thing called in main so Lockdown | |
# will exit before it evaluates $cmd | |
echo " SIP: ${sip}" | |
echo " T2: ${t2_mac}" | |
echo | |
exit 0 | |
} | |
function full_disk_access_check { | |
if [ -r "$HOME/Library/Mail" ]; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
function version { | |
echo "${LOCKDOWN_VERSION}" | |
} | |
### END UTILITY FUNCTIONS ### | |
function enable_automatic_system_updates { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Enable Automatic System Updates" | |
# shellcheck disable=SC2016 | |
audit_command='if ! defaults read "/Library/Preferences/com.apple.SoftwareUpdate.plist" "AutomaticallyInstallMacOSUpdates" >/dev/null 2>&1; then exit 1; fi; defaults read "/Library/Preferences/com.apple.SoftwareUpdate.plist" AutomaticallyInstallMacOSUpdates | grep -q "1"' | |
# shellcheck disable=SC2016 | |
fix_command='declare -a keys; keys=(AutomaticCheckEnabled AutomaticDownload AutomaticallyInstallMacOSUpdates ConfigDataInstall CriticalUpdateInstall); for key in "${keys[@]}"; do sudo defaults write "/Library/Preferences/com.apple.SoftwareUpdate.plist" "${key}" -bool true; done' | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_automatic_app_store_updates { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Enable Automatic App Store Updates" | |
audit_command='if ! defaults read "/Library/Preferences/com.apple.commerce.plist" "AutoUpdate" >/dev/null 2>&1; then exit 1; fi; defaults read "/Library/Preferences/com.apple.commerce.plist" "AutoUpdate" | grep -q "1"' | |
fix_command="sudo defaults write '/Library/Preferences/com.apple.commerce.plist' 'AutoUpdate' -bool true" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_gatekeeper { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Enable Gatekeeper" | |
audit_command='spctl --status | grep -q "assessments enabled"' | |
fix_command='sudo spctl --master-enable' | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_firewall { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Enable Firewall" | |
audit_command="sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate | grep -q 'enabled'" | |
fix_command="sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on >/dev/null" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_admin_password_preferences { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Require an administrator password to access system-wide preferences" | |
audit_command="security -q authorizationdb read system.preferences | grep -A1 'shared' | grep -q 'false'" | |
fix_command="security -q authorizationdb read system.preferences > /tmp/system.preferences.plist; /usr/libexec/PlistBuddy -c 'Set :shared false' /tmp/system.preferences.plist; sudo security -q authorizationdb write system.preferences < /tmp/system.preferences.plist; rm '/tmp/system.preferences.plist'" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_terminal_secure_entry { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Enable Terminal.app secure keyboard entry" | |
audit_command="defaults read com.apple.Terminal SecureKeyboardEntry | grep -q '1'" | |
fix_command="defaults write com.apple.Terminal SecureKeyboardEntry -bool true" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_sip { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Enable System Integrity Protection (SIP)" | |
audit_command="csrutil status | grep -q 'enabled'" | |
fix_command="sudo csrutil clear >/dev/null" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function enable_filevault { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
local permission="n" | |
title="Enable FileVault" | |
audit_command="fdesetup status | grep -q 'On'" | |
fix_command="sudo fdesetup enable -user $USER > $HOME/FileVault_recovery_key.txt" | |
if [[ "${mode}" == "fix" ]]; then | |
if ! /usr/sbin/diskutil info / | /usr/bin/grep 'File System Personality:' | /usr/bin/grep -q 'APFS'; then | |
# Only offer to enable if the filesystem is APFS | |
return 1 | |
elif [[ "${cmd}" == "fix-force" ]]; then | |
# Exit if Lockdown invoked with fix-force | |
# Enabling FDE should be explicit, don't want anyone to do this by accident | |
echo " [⚠️ ] ${BOLD}Didn't fix${RESET}: ${title}" | |
return 1 | |
fi | |
echo -en "\\n [⚠️ ] Do you want to ${RED}enable FileVault${RESET}? (y/${BOLD}N${RESET}) " | |
read -r permission | |
if [[ "${permission}" =~ ^(y|Y)$ ]]; then | |
echo " [✅] ${USER} has chosen to enable FileVault" | |
echo -e " [⚠️ ] Recovery key saved to ${RED}$HOME/FileVault_recovery_key.txt${RESET}\\n" | |
else | |
echo -e " [❌] ${USER} has chosen ${BOLD}not${RESET} to enable FileVault\\n" | |
return 1 | |
fi | |
fi | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_firewall_builin_software { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable built-in software from being auto-permitted to listen through firewall" | |
audit_command="sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getallowsigned | grep 'built-in' | grep -q 'DISABLED'" | |
fix_command="sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsigned off >/dev/null" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_firewall_downloaded_signed { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable downloaded signed software from being auto-permitted to listen through firewall" | |
audit_command="sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getallowsigned | grep 'downloaded' | grep -q 'DISABLED'" | |
fix_command="sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsignedapp off >/dev/null" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_ipv6 { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable IPv6" | |
# shellcheck disable=SC2016 | |
audit_command='while IFS= read -r i; do if ! networksetup -getinfo "${i}" | grep -q "IPv6: Off"; then exit 1; fi; done <<< $(networksetup -listallnetworkservices | tail -n $(( $(networksetup -listallnetworkservices | wc -l) - 1 )))' | |
# shellcheck disable=SC2016 | |
fix_command='while read -r i; do sudo networksetup -setv6off "${i}"; done <<< "$(networksetup -listallnetworkservices | tail -n $(( $(networksetup -listallnetworkservices | wc -l) - 1 )))"' | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_mail_remote_content { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable automatic loading of remote content by Mail.app" | |
if ! full_disk_access_check; then | |
echo " [⚠️ ] ${title} (${RED}Requires 'Full Disk Access' permission${RESET})" | |
return 1 | |
fi | |
audit_command="defaults read com.apple.mail-shared DisableURLLoading 2>/dev/null | grep -q '1'" | |
fix_command="defaults write com.apple.mail-shared DisableURLLoading -bool true" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_remote_apple_events { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable Remote Apple Events" | |
audit_command="sudo systemsetup -getremoteappleevents | grep -q 'Remote Apple Events: Off'" | |
fix_command="sudo systemsetup -setremoteappleevents off >/dev/null" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_remote_login { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable Remote Login" | |
audit_command="sudo systemsetup -getremotelogin | grep -q 'Remote Login: Off'" | |
fix_command="sudo systemsetup -f -setremotelogin off >/dev/null" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function disable_auto_open_safe_downloads { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Disable Safari Auto Open 'safe' Files" | |
if ! full_disk_access_check; then | |
echo " [⚠️ ] ${title} (${RED}Requires 'Full Disk Access' permission${RESET})" | |
return 1 | |
fi | |
audit_command="defaults read com.apple.Safari AutoOpenSafeDownloads 2>/dev/null | grep -q '0'" | |
fix_command="defaults write com.apple.Safari AutoOpenSafeDownloads -bool false" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function set_airdrop_contacts_only { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Set AirDrop Discoverability to 'Contacts Only'" | |
audit_command="if defaults read com.apple.sharingd DiscoverableMode 2>/dev/null | grep -q 'Contacts Only'; then exit 0; elif defaults read com.apple.sharingd DiscoverableMode 2>/dev/null | grep -q 'Off'; then exit 0; else exit 1; fi" | |
fix_command="defaults write com.apple.sharingd DiscoverableMode -string 'Contacts Only' \ | |
&& sudo killall -HUP sharingd" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function set_appstore_update_check_daily { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Set AppStore update check to every one (1) day" | |
audit_command="defaults read com.apple.SoftwareUpdate ScheduleFrequency 2>/dev/null | grep -q '1'" | |
fix_command="defaults write com.apple.SoftwareUpdate ScheduleFrequency -int 1" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function check_kext_loading_consent { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Check Kernel Extension User Consent required" | |
audit_command="spctl kext-consent status | grep -q 'ENABLED'" | |
fix_command='' | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function check_efi_integrity { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="Check EFI Firmware Integrity" | |
audit_command="/usr/libexec/firmwarecheckers/eficheck/eficheck --integrity-check >/dev/null 2>&1" | |
fix_command='' | |
if /usr/sbin/system_profiler SPiBridgeDataType | /usr/bin/grep 'Model Name:' | /usr/bin/grep -q 'T2'; then | |
echo " [⚠️ ] ${title} (${RED}Not supported on Macs with T2 chips${RESET})" | |
t2_mac="True" | |
return 1 | |
fi | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function set_firmware_password { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
local permission="n" | |
title="Set a firmware password" | |
audit_command="sudo firmwarepasswd -check | grep -q 'Yes'" | |
fix_command="sudo firmwarepasswd -setpasswd" | |
if check_if_vm; then | |
echo " [⚠️ ] ${title} (${RED}Can't set a firmware password in a VM${RESET})" | |
return 1 | |
fi | |
if [[ "${mode}" == "fix" ]]; then | |
if [[ "${cmd}" == "fix-force" ]]; then | |
# Exit if Lockdown invoked with fix-force | |
# Enabling FDE should be explicit, don't want anyone to do this by accident | |
echo " [⚠️ ] ${BOLD}Didn't fix${RESET}: ${title}" | |
return 1 | |
fi | |
echo -en "\\n [⚠️ ] Do you want to ${RED}set a Firmware password?${RESET} (y/${BOLD}N${RESET}) " | |
read -r permission | |
if [[ "${permission}" =~ ^(y|Y)$ ]]; then | |
echo " [✅] ${USER} has chosen to set a firmware password" | |
else | |
echo -e " [❌] ${USER} has chosen ${BOLD}not${RESET} to set a firmware password\\n" | |
return 1 | |
fi | |
fi | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
function check_if_standard_user { | |
local mode=${1:?No mode passed} | |
local title | |
local audit_command | |
local fix_command | |
title="${USER} should not be an administrator" | |
audit_command="groups | grep -qv 'admin'" | |
fix_command="" | |
mode_check "${mode}" "${title}" "${audit_command}" "${fix_command}" | |
} | |
############################ | |
function main { | |
verify_signature | |
# Verify Lockdown signature | |
macos_compatability_check | |
# Check the system is running the supoorted version of macOS | |
declare -r cmd=${1:-"usage"} | |
declare -a settings | |
declare -i setting_index=-1 | |
declare audit_setting_num | |
declare fix_setting_num | |
declare t2_mac="False" | |
declare is_vm="False" | |
declare is_codesigned="False" | |
declare sip="False" | |
settings=(enable_automatic_system_updates enable_automatic_app_store_updates enable_gatekeeper enable_firewall enable_admin_password_preferences \ | |
enable_terminal_secure_entry enable_sip enable_filevault \ | |
disable_firewall_builin_software disable_firewall_downloaded_signed \ | |
disable_ipv6 disable_mail_remote_content disable_remote_apple_events disable_remote_login \ | |
disable_auto_open_safe_downloads set_airdrop_contacts_only set_appstore_update_check_daily \ | |
set_firmware_password check_kext_loading_consent check_efi_integrity check_if_standard_user) | |
trap ctrl_c SIGINT | |
# Detect and react to the user hitting CTRL + C | |
case "${cmd}" in | |
list) | |
echo -e "\\nSettings (${BOLD}${#settings[@]}${RESET}) that can be audited or fixed: " | |
for setting in "${settings[@]}"; do | |
setting_index=$((setting_index+1)) | |
# shellcheck disable=SC2116 | |
setting_read="$(echo "${setting//_/ }")" | |
# Replace underscore with a space, more human readable | |
echo " (${RED}${setting_index}${RESET}) ${setting_read}" | |
done | |
echo | |
exit 0 | |
;; | |
audit) | |
audit_setting_num=${2:--1} | |
check_index "${audit_setting_num}" | |
sudo_prompt | |
echo -e "\\nResults: " | |
if ! [ "${audit_setting_num}" -lt 0 ]; then | |
"${settings[${audit_setting_num}]}" "audit" | |
else | |
for setting in "${settings[@]}"; do | |
"${setting}" "audit" | |
# Call functions in 'settings' array with the argument 'audit' | |
done | |
fi | |
echo | |
;; | |
fix|fix-force) | |
fix_setting_num=${2:--1} | |
check_index "${fix_setting_num}" | |
if [[ "${cmd}" != "fix-force" ]]; then | |
# Confirm the user wants to run FIX mode | |
# If "fix force" skip the prompt | |
get_fix_mode_permission | |
fi | |
echo -e "\\nResults: " | |
if ! [ "${fix_setting_num}" -lt 0 ]; then | |
"${settings[${fix_setting_num}]}" "audit" | |
else | |
for setting in "${settings[@]}"; do | |
if ! "${setting}" "audit" >/dev/null; then | |
# Run the audit command first | |
# Only run the fix command if audit fails | |
"${setting}" "fix" | |
fi | |
done | |
fi | |
echo | |
;; | |
debug) | |
debug | |
;; | |
usage|help|-h|--help|🤷♂️|🤷♀️) | |
usage | |
;; | |
version|-v|--version) | |
version | |
;; | |
*) | |
echo -e "\\n [❌] ${RED}Invalid command:${RESET} ${cmd}" | |
exit 1 | |
;; | |
esac | |
} | |
main "$@" |