From 798d40aacedd984657f3307ff8b7ec03018d4ebc Mon Sep 17 00:00:00 2001 From: Derrek Landauer Date: Wed, 13 Mar 2024 13:18:22 -0600 Subject: [PATCH 1/6] Added Arch Linux Support --- cac_setup_arch.sh | 469 ++++++++++++++++++++++++++++ cac_setup.sh => cac_setup_debian.sh | 0 2 files changed, 469 insertions(+) create mode 100755 cac_setup_arch.sh rename cac_setup.sh => cac_setup_debian.sh (100%) diff --git a/cac_setup_arch.sh b/cac_setup_arch.sh new file mode 100755 index 0000000..7e481f0 --- /dev/null +++ b/cac_setup_arch.sh @@ -0,0 +1,469 @@ +#!/usr/bin/env bash + +# cac_setup.sh +# Description: Setup a Linux environment for Common Access Card use. + +main () +{ + EXIT_SUCCESS=0 # Success exit code + E_INSTALL=85 # Installation failed + E_NOTROOT=86 # Non-root exit error + E_BROWSER=87 # Compatible browser not found + E_DATABASE=88 # No database located + DWNLD_DIR="/tmp" # Reliable location to place artifacts + FF_PROFILE_NAME="old_ff_profile" # Reliable location to place artifacts + + chrome_exists=false # Google Chrome is installed + ff_exists=false # Firefox is installed + + ORIG_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)" + CERT_EXTENSION="cer" + PKCS_FILENAME="pkcs11.txt" + DB_FILENAME="cert9.db" + CERT_FILENAME="AllCerts" + BUNDLE_FILENAME="AllCerts.zip" + CERT_URL="http://militarycac.com/maccerts/$BUNDLE_FILENAME" + PKG_FILENAME="cackey_0.7.5-1_amd64.pkg.tar.xz" + CACKEY_URL="https://aur.archlinux.org/cgit/aur.git/snapshot/cackey.tar.gz" + + root_check + browser_check + + mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash") + # Check if databases were found properly + if [ "${#databases[@]}" -eq 0 ] + then + # Database was not found + print_err "No valid databases located. Try running, then closing Firefox, then start this script again." + echo -e "\tExiting..." + + exit "$E_DATABASE" + fi + +# Install middleware and necessary utilities +print_info "Installing middleware..." +pacman -Syu --noconfirm +pacman -S --noconfirm ccid opensc pcsc-tools nss unzip wget +print_info "Done" + +# Pull all necessary files +print_info "Downloading DoD certificates and Cackey package..." +wget -qP "$DWNLD_DIR" "$CERT_URL" +wget -qP "$DWNLD_DIR" "$CACKEY_URL" +print_info "Done." + + +# Install libcackey. +if command -v yay >/dev/null 2>&1; then + print_info "Installing libcackey using yay..." + sudo -u "$SUDO_USER" yay -S --noconfirm cackey +elif command -v paru >/dev/null 2>&1; then + print_info "Installing libcackey using paru..." + sudo -u "$SUDO_USER" paru -S --noconfirm cackey +else + print_err "Neither yay nor paru is available. Please install one of them and try again." + exit 1 +fi + +# Check if libcackey.so is installed +libcackey_path=$(find /usr -name "libcackey.so" | head -n 1) +if [ -z "$libcackey_path" ]; then + print_err "libcackey.so not found after installation. Please check the installation manually." + exit 1 +else + print_info "libcackey.so found at $libcackey_path" +fi + +# Unzip cert bundle +if [ -e "$DWNLD_DIR/$BUNDLE_FILENAME" ] +then + mkdir -p "$DWNLD_DIR/$CERT_FILENAME" + unzip "$DWNLD_DIR/$BUNDLE_FILENAME" -d "$DWNLD_DIR/$CERT_FILENAME" +fi + +# Import certificates into cert9.db databases for browsers +for db in "${databases[@]}" +do + if [ -n "$db" ] + then + import_certs "$db" + fi +done + +print_info "Enabling pcscd service to start on boot..." +systemctl enable pcscd.socket + +# Remove artifacts +print_info "Removing artifacts..." +rm -rf "${DWNLD_DIR:?}"/{"$BUNDLE_FILENAME","$CERT_FILENAME","$PKG_FILENAME","$FF_PROFILE_NAME"} 2>/dev/null +if [ "$?" -ne "$EXIT_SUCCESS" ] +then + print_err "Failed to remove artifacts" +else + print_info "Done." +fi + +exit "$EXIT_SUCCESS" +} # main + +# Prints message with red [ERROR] tag before the message +print_err () +{ + ERR_COLOR='\033[0;31m' # Red for error messages + NO_COLOR='\033[0m' # Revert terminal back to no color + + echo -e "${ERR_COLOR}[ERROR]${NO_COLOR} $1" +} # print_err + +# Prints message with yellow [INFO] tag before the message +print_info () +{ + INFO_COLOR='\033[0;33m' # Yellow for notes + NO_COLOR='\033[0m' # Revert terminal back to no color + + echo -e "${INFO_COLOR}[INFO]${NO_COLOR} $1" +} # print_info + +# Check to ensure the script is executed as root +root_check () +{ + local ROOT_UID=0 # Only users with $UID 0 have root privileges + + # Ensure the script is ran as root + if [ "${EUID:-$(id -u)}" -ne "$ROOT_UID" ] + then + print_err "Please run this script as root." + exit "$E_NOTROOT" + fi +} # root_check + +# Replace the current snap version of Firefox with the compatible apt version of Firefox +reconfigure_firefox () +{ + # Replace snap Firefox with version from PPA maintained via Mozilla + + check_for_ff_pin + + #Profile migration + backup_ff_profile + + print_info "Removing Snap version of Firefox" + snap remove --purge firefox + + print_info "Adding PPA for Mozilla maintained Firefox" + add-apt-repository -y ppa:mozillateam/ppa + + print_info "Setting priority to prefer Mozilla PPA over snap package" + echo -e "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox + + print_info "Enabling updates for future Firefox releases" + # shellcheck disable=SC2016 + echo -e 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' > /etc/apt/apt.conf.d/51unattended-upgrades-firefox + + print_info "Installing Firefox via apt" + apt install firefox -y + print_info "Completed re-installation of Firefox" + + # Forget the previous location of firefox executable + if hash firefox + then + hash -d firefox + fi + + run_firefox + + print_info "Finished, closing Firefox." + + if [ "$backup_exists" == true ] + then + print_info "Migrating user profile into newly installed Firefox" + migrate_ff_profile "migrate" + fi + + repin_firefox + +} # reconfigure_firefox + +run_firefox () +{ + print_info "Starting Firefox silently to complete post-install actions..." + sudo -H -u "$SUDO_USER" firefox --headless --first-startup >/dev/null 2>&1 & + sleep 3 + pkill -9 firefox + sleep 1 +} + +# Discovery of browsers installed on the user's system +# Sets appropriate flags to control the flow of the installation, depending on +# what is needed for the individual user +browser_check () +{ + print_info "Checking for Firefox and Chrome..." + check_for_firefox + check_for_chrome + + # Browser check results + if [ "$ff_exists" == false ] && [ "$chrome_exists" == false ] + then + print_err "No version of Mozilla Firefox OR Google Chrome has been detected." + print_info "Please install either or both to proceed." + + exit "$E_BROWSER" + + elif [ "$ff_exists" == true ] # Firefox was found + then + if [ "$snap_ff" == true ] # Snap version of Firefox + then + echo -e " + ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}******************** + * The version of Firefox you have installed * + * currently was installed via snap. * + * This version of Firefox is not currently * + * compatible with the method used to enable CAC * + * support in browsers. * + * * + * As a work-around, this script can automatically * + * remove the snap version and reinstall via apt. * + * * + * The option to attempt to migrate all of your * + * personalizations will be given if you choose to * + * replace Firefox via this script. Your Firefox * + * profile will be saved to a temp location, then * + * will overwrite the default profile once the apt * + * version of Firefox has been installed. * + * * + ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}********************\n" + + # Prompt user to elect to replace snap firefox with apt firefox + choice='' + while [ "$choice" != "y" ] && [ "$choice" != "n" ] + do + echo -e "\nWould you like to switch to the apt version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" + read -rp '> ' choice + done + + if [ "$choice" == "y" ] + then + reconfigure_firefox + else + if [ $chrome_exists == false ] + then + print_info "You have elected to keep the snap version of Firefox.\n" + print_err "You have no compatible browsers. Exiting..." + + exit $E_BROWSER + fi + fi + fi + fi + } + +# Locate and backup the profile for the user's snap version of Firefox +# Backup is placed in /tmp/ff_old_profile/ and can be restored after the +# apt version of Firefox has been installed +backup_ff_profile () +{ + location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" + if [ -z "$location" ] + then + print_info "No user profile was found in snap-installed version of Firefox." + else + # A user profile exists in the snap version of FF + choice='' + while [ "$choice" != "y" ] && [ "$choice" != "n" ] + do + echo -e "\nWould you like to transfer your bookmarks and personalizations to the new version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" + read -rp '> ' choice + done + + if [ "$choice" == "y" ] + then + print_info "Backing up Firefox profile" + ff_profile="$(dirname "$location")" + sudo -H -u "$SUDO_USER" cp -rf "$ff_profile" "$DWNLD_DIR/$FF_PROFILE_NAME" + backup_exists=1 + fi + + fi +} # backup_ff_profile + +# Moves the user's backed up Firefox profile from the temp location to the newly +# installed apt version of Firefox in the ~/.mozilla directory +# TODO: Take arguments for source and destination so profile can be restored to +# original location in the event of a failed install +migrate_ff_profile () +{ + direction=$1 + + if [ "$direction" == "migrate" ] + then + apt_ff_profile="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep -v snap)" + if [ -z "$apt_ff_profile" ] + then + print_err "Something went wrong while trying to find apt Firefox's user profile directory." + exit "$E_DATABASE" + else + ff_profile_dir="$(dirname "$apt_ff_profile")" + if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" + then + print_info "Successfully migrated user profile for Firefox versions" + else + print_err "Unable to migrate Firefox profile" + fi + fi + elif [ "$direction" == "restore" ] + then + location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" + if [ -z "$location" ] + then + print_info "No user profile was found in snap-installed version of Firefox." + else + ff_profile_dir="$(dirname "$apt_ff_profile")" + if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" + then + print_info "Successfully restored user profile for Firefox" + else + print_err "Unable to migrate Firefox profile" + fi + fi + fi + +} + +# Attempt to find an installed version of Firefox on the user's system +# Determines whether the version is installed via snap or apt +check_for_firefox () +{ + if command -v firefox >/dev/null + then + ff_exists=true + print_info "Found Firefox." + if command -v firefox | grep snap >/dev/null + then + snap_ff=true + print_err "This version of Firefox was installed as a snap package" + else + # Run Firefox to ensure .mozilla directory has been created + echo -e "Running Firefox to generate profile directory..." + sudo -H -u "$SUDO_USER" bash -c 'firefox --headless --first-startup >/dev/null 2>&1 &' + sleep 3 + pkill -9 firefox + sleep 1 + echo -e "\tDone." + fi + else + print_info "Firefox not found." + fi +} +# Attempt to find a version of Google Chrome installed on the user's system +check_for_chrome () +{ + # Check to see if Chrome exists + if command -v google-chrome-stable >/dev/null + then + chrome_exists=true + print_info "Found Google Chrome." + else + print_info "Chrome not found." + fi +} + +# Re-install the user's previous version of Firefox if the snap version was +# removed in the process of this script. +revert_firefox () +{ + # Firefox was replaced, lets put it back where it was. + print_err "No valid databases located. Reinstalling previous version of Firefox..." + apt purge firefox -y + snap install firefox + + run_firefox + + print_info "Completed. Exiting..." + + # "Restore" old profile back to the snap version of Firefox + migrate_ff_profile "restore" + + exit "$E_DATABASE" +} + +# Integrate all certificates into the databases for existing browsers +import_certs () +{ + db=$1 + db_root="$(dirname "$db")" + if [ -n "$db_root" ] + then + case "$db_root" in + *"pki"*) + print_info "Importing certificates for Chrome..." + echo + ;; + *"firefox"*) + print_info "Importing certificates for Firefox..." + echo + ;; + esac + + print_info "Loading certificates into $db_root " + echo + + for cert in "$DWNLD_DIR/$CERT_FILENAME/"*."$CERT_EXTENSION" + do + echo "Importing $cert" + certutil -d sql:"$db_root" -A -t TC -n "$cert" -i "$cert" + done + + libcackey_path=$(ldconfig -p | awk '/libcackey\.so/ {print $NF}') + if [ -n "$libcackey_path" ]; then + if ! grep -Pzo "library=$libcackey_path\nname=CAC Module\n" "$db_root/$PKCS_FILENAME" >/dev/null + then + printf "library=$libcackey_path\nname=CAC Module\n" >> "$db_root/$PKCS_FILENAME" + fi + else + print_err "libcackey.so not found. Please ensure the cackey package is installed correctly." + fi + fi + + echo "Done." + echo +} # import_certs + +# Check to see if the user has Firefox pinned to their favorites bar in GNOME +check_for_ff_pin () +{ + # firefox is a favorite and if gnome is the desktop environment. + + if sudo -u "$SUDO_USER" bash -c "$(echo "$XDG_CURRENT_DESKTOP" | grep "GNOME" >/dev/null 2>&1)" + then + print_info "Detected Gnome desktop environment" + if echo "$curr_favorites" | grep "firefox.desktop" >/dev/null 2>&1 + then + ff_was_pinned=true + else + print_info "Firefox not pinned to favorites" + fi + else + print_err "Desktop environment not yet supported." + print_err "Unable to repin Firefox to favorites bar" + print_info "Firefox can still be repinned manually" + fi +} # check_for_ff_pin + +repin_firefox () +{ + print_info "Attempting to repin Firefox to favorites bar..." + if [ "$ff_was_pinned" == true ] + then + # TODO: finish this + + curr_favorites=$(gsettings get org.gnome.shell favorite-apps) + print_info "Repinning Firefox to favorites bar" + + gsettings set org.gnome.shell favorite-apps "$(gsettings get org.gnome.shell favorite-apps | sed s/.$//), 'firefox.desktop']" + + print_info "Done." + fi +} # repin_firefox + +main diff --git a/cac_setup.sh b/cac_setup_debian.sh similarity index 100% rename from cac_setup.sh rename to cac_setup_debian.sh From c4f90f7572b4cb8c7d7caf4d00a5d40e41578508 Mon Sep 17 00:00:00 2001 From: Derrek Landauer Date: Wed, 13 Mar 2024 14:47:28 -0600 Subject: [PATCH 2/6] merged debian and arch scripts --- cac_setup.sh | 586 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100755 cac_setup.sh diff --git a/cac_setup.sh b/cac_setup.sh new file mode 100755 index 0000000..4dbdc90 --- /dev/null +++ b/cac_setup.sh @@ -0,0 +1,586 @@ +#!/usr/bin/env bash + +# cac_setup.sh +# Description: Setup a Linux environment for Common Access Card use. + +main () +{ + EXIT_SUCCESS=0 # Success exit code + E_INSTALL=85 # Installation failed + E_NOTROOT=86 # Non-root exit error + E_BROWSER=87 # Compatible browser not found + E_DATABASE=88 # No database located + DWNLD_DIR="/tmp" # Reliable location to place artifacts + FF_PROFILE_NAME="old_ff_profile" # Reliable location to place artifacts + + chrome_exists=false # Google Chrome is installed + ff_exists=false # Firefox is installed + snap_ff=false # Flag to prompt for how to handle snap Firefox + + ORIG_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)" + CERT_EXTENSION="cer" + PKCS_FILENAME="pkcs11.txt" + DB_FILENAME="cert9.db" + CERT_FILENAME="AllCerts" + BUNDLE_FILENAME="AllCerts.zip" + CERT_URL="http://militarycac.com/maccerts/$BUNDLE_FILENAME" + PKG_FILENAME="cackey_0.7.5-1_amd64.deb" + CACKEY_URL="http://cackey.rkeene.org/download/0.7.5/$PKG_FILENAME" + + root_check + browser_check + + check_distro + + if [ "$distro" == "debian" ]; then + setup_debian + elif [ "$distro" == "arch" ]; then + setup_arch + else + echo "The current distribution is not supported by this script." + exit 1 + fi + + # Unzip cert bundle + if [ -e "$DWNLD_DIR/$BUNDLE_FILENAME" ] + then + mkdir -p "$DWNLD_DIR/$CERT_FILENAME" + unzip "$DWNLD_DIR/$BUNDLE_FILENAME" -d "$DWNLD_DIR/$CERT_FILENAME" + fi + + # Import certificates into cert9.db databases for browsers + for db in "${databases[@]}" + do + if [ -n "$db" ] + then + import_certs "$db" + fi + done + + print_info "Enabling pcscd service to start on boot..." + systemctl enable pcscd.socket + + # Remove artifacts + print_info "Removing artifacts..." + rm -rf "${DWNLD_DIR:?}"/{"$BUNDLE_FILENAME","$CERT_FILENAME","$PKG_FILENAME","$FF_PROFILE_NAME"} 2>/dev/null + if [ "$?" -ne "$EXIT_SUCCESS" ] + then + print_err "Failed to remove artifacts" + else + print_info "Done." + fi + + exit "$EXIT_SUCCESS" +} # main + +# Prints message with red [ERROR] tag before the message +print_err () +{ + ERR_COLOR='\033[0;31m' # Red for error messages + NO_COLOR='\033[0m' # Revert terminal back to no color + + echo -e "${ERR_COLOR}[ERROR]${NO_COLOR} $1" +} # print_err + +# Prints message with yellow [INFO] tag before the message +print_info () +{ + INFO_COLOR='\033[0;33m' # Yellow for notes + NO_COLOR='\033[0m' # Revert terminal back to no color + + echo -e "${INFO_COLOR}[INFO]${NO_COLOR} $1" +} # print_info + +# Check to ensure the script is executed as root +root_check () +{ + local ROOT_UID=0 # Only users with $UID 0 have root privileges + + # Ensure the script is ran as root + if [ "${EUID:-$(id -u)}" -ne "$ROOT_UID" ] + then + print_err "Please run this script as root." + exit "$E_NOTROOT" + fi +} # root_check + +# Replace the current snap version of Firefox with the compatible apt version of Firefox +reconfigure_firefox () +{ + # Replace snap Firefox with version from PPA maintained via Mozilla + + check_for_ff_pin + + #Profile migration + backup_ff_profile + + print_info "Removing Snap version of Firefox" + snap remove --purge firefox + + print_info "Adding PPA for Mozilla maintained Firefox" + add-apt-repository -y ppa:mozillateam/ppa + + print_info "Setting priority to prefer Mozilla PPA over snap package" + echo -e "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox + + print_info "Enabling updates for future Firefox releases" + # shellcheck disable=SC2016 + echo -e 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' > /etc/apt/apt.conf.d/51unattended-upgrades-firefox + + print_info "Installing Firefox via apt" + apt install firefox -y + print_info "Completed re-installation of Firefox" + + # Forget the previous location of firefox executable + if hash firefox + then + hash -d firefox + fi + + run_firefox + + print_info "Finished, closing Firefox." + + if [ "$backup_exists" == true ] + then + print_info "Migrating user profile into newly installed Firefox" + migrate_ff_profile "migrate" + fi + + repin_firefox + +} # reconfigure_firefox + +run_firefox () +{ + print_info "Starting Firefox silently to complete post-install actions..." + sudo -H -u "$SUDO_USER" firefox --headless --first-startup >/dev/null 2>&1 & + sleep 3 + pkill -9 firefox + sleep 1 +} + +# Discovery of browsers installed on the user's system +# Sets appropriate flags to control the flow of the installation, depending on +# what is needed for the individual user +browser_check () +{ + print_info "Checking for Firefox and Chrome..." + check_for_firefox + + if [ "$distro" == "debian" ]; then + check_for_chrome_debian + elif [ "$distro" == "arch" ]; then + check_for_chrome_arch + fi + + # Browser check results + if [ "$ff_exists" == false ] && [ "$chrome_exists" == false ] + then + print_err "No version of Mozilla Firefox OR Google Chrome has been detected." + print_info "Please install either or both to proceed." + + exit "$E_BROWSER" + + elif [ "$ff_exists" == true ] # Firefox was found + then + if [ "$snap_ff" == true ] # Snap version of Firefox + then + echo -e " + ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}******************** + * The version of Firefox you have installed * + * currently was installed via snap. * + * This version of Firefox is not currently * + * compatible with the method used to enable CAC * + * support in browsers. * + * * + * As a work-around, this script can automatically * + * remove the snap version and reinstall via apt. * + * * + * The option to attempt to migrate all of your * + * personalizations will be given if you choose to * + * replace Firefox via this script. Your Firefox * + * profile will be saved to a temp location, then * + * will overwrite the default profile once the apt * + * version of Firefox has been installed. * + * * + ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}********************\n" + + # Prompt user to elect to replace snap firefox with apt firefox + choice='' + while [ "$choice" != "y" ] && [ "$choice" != "n" ] + do + echo -e "\nWould you like to switch to the apt version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" + read -rp '> ' choice + done + + if [ "$choice" == "y" ] + then + reconfigure_firefox + else + if [ $chrome_exists == false ] + then + print_info "You have elected to keep the snap version of Firefox.\n" + print_err "You have no compatible browsers. Exiting..." + + exit $E_BROWSER + fi + fi + fi + fi +} + +# Locate and backup the profile for the user's snap version of Firefox +# Backup is placed in /tmp/ff_old_profile/ and can be restored after the +# apt version of Firefox has been installed +backup_ff_profile () +{ + location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" + if [ -z "$location" ] + then + print_info "No user profile was found in snap-installed version of Firefox." + else + # A user profile exists in the snap version of FF + choice='' + while [ "$choice" != "y" ] && [ "$choice" != "n" ] + do + echo -e "\nWould you like to transfer your bookmarks and personalizations to the new version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" + read -rp '> ' choice + done + + if [ "$choice" == "y" ] + then + print_info "Backing up Firefox profile" + ff_profile="$(dirname "$location")" + sudo -H -u "$SUDO_USER" cp -rf "$ff_profile" "$DWNLD_DIR/$FF_PROFILE_NAME" + backup_exists=1 + fi + + fi +} # backup_ff_profile + +# Moves the user's backed up Firefox profile from the temp location to the newly +# installed apt version of Firefox in the ~/.mozilla directory +# TODO: Take arguments for source and destination so profile can be restored to +# original location in the event of a failed install +migrate_ff_profile () +{ + direction=$1 + + if [ "$direction" == "migrate" ] + then + apt_ff_profile="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep -v snap)" + if [ -z "$apt_ff_profile" ] + then + print_err "Something went wrong while trying to find apt Firefox's user profile directory." + exit "$E_DATABASE" + else + ff_profile_dir="$(dirname "$apt_ff_profile")" + if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" + then + print_info "Successfully migrated user profile for Firefox versions" + else + print_err "Unable to migrate Firefox profile" + fi + fi + elif [ "$direction" == "restore" ] + then + location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" + if [ -z "$location" ] + then + print_info "No user profile was found in snap-installed version of Firefox." + else + ff_profile_dir="$(dirname "$apt_ff_profile")" + if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" + then + print_info "Successfully restored user profile for Firefox" + else + print_err "Unable to migrate Firefox profile" + fi + fi + fi + +} + +# Attempt to find an installed version of Firefox on the user's system +# Determines whether the version is installed via snap or apt +check_for_firefox () +{ + if command -v firefox >/dev/null + then + ff_exists=true + print_info "Found Firefox." + if command -v firefox | grep snap >/dev/null + then + snap_ff=true + print_err "This version of Firefox was installed as a snap package" + else + # Run Firefox to ensure .mozilla directory has been created + echo -e "Running Firefox to generate profile directory..." + sudo -H -u "$SUDO_USER" bash -c 'firefox --headless --first-startup >/dev/null 2>&1 &' + sleep 3 + pkill -9 firefox + sleep 1 + echo -e "\tDone." + fi + else + print_info "Firefox not found." + fi +} +# Attempt to find a version of Google Chrome installed on the user's system +check_for_chrome_debian () +{ + # Check to see if Chrome exists + if command -v google-chrome >/dev/null + then + chrome_exists=true + print_info "Found Google Chrome." + # Run Chrome to ensure .pki directory has been created +# echo -e "\tRunning Chrome to ensure it has completed post-install actions..." +# # TODO: finish troubleshooting this +# sudo -H -u "$SUDO_USER" bash -c 'google-chrome --headless --disable-gpu >/dev/null 2>&1 &' +# sleep 3 +# pkill -9 google-chrome +# sleep 1 +# echo -e "\tDone." + else + print_info "Chrome not found." + fi +} + + +check_for_chrome_arch () +{ + # Check to see if Chrome exists + if command -v google-chrome-stable >/dev/null + then + chrome_exists=true + print_info "Found Google Chrome." + else + print_info "Chrome not found." + fi +} + +# Re-install the user's previous version of Firefox if the snap version was +# removed in the process of this script. +revert_firefox () +{ + # Firefox was replaced, lets put it back where it was. + print_err "No valid databases located. Reinstalling previous version of Firefox..." + apt purge firefox -y + snap install firefox + + run_firefox + + print_info "Completed. Exiting..." + + # "Restore" old profile back to the snap version of Firefox + migrate_ff_profile "restore" + + exit "$E_DATABASE" +} + +# Integrate all certificates into the databases for existing browsers + import_certs () +{ + db=$1 + db_root="$(dirname "$db")" + if [ -n "$db_root" ] + then + case "$db_root" in + *"pki"*) + print_info "Importing certificates for Chrome..." + echo + ;; + *"firefox"*) + print_info "Importing certificates for Firefox..." + echo + ;; + esac + + print_info "Loading certificates into $db_root " + echo + + for cert in "$DWNLD_DIR/$CERT_FILENAME/"*."$CERT_EXTENSION" + do + echo "Importing $cert" + certutil -d sql:"$db_root" -A -t TC -n "$cert" -i "$cert" + done + + if ! grep -Pzo 'library=/usr/lib64/libcackey.so\nname=CAC Module\n' "$db_root/$PKCS_FILENAME" >/dev/null + then + printf "library=/usr/lib64/libcackey.so\nname=CAC Module\n" >> "$db_root/$PKCS_FILENAME" + fi + fi + + echo "Done." + echo +} # import_certs + +# Check to see if the user has Firefox pinned to their favorites bar in GNOME +check_for_ff_pin () +{ + # firefox is a favorite and if gnome is the desktop environment. + + if sudo -u "$SUDO_USER" bash -c "$(echo "$XDG_CURRENT_DESKTOP" | grep "GNOME" >/dev/null 2>&1)" + then + print_info "Detected Gnome desktop environment" + if echo "$curr_favorites" | grep "firefox.desktop" >/dev/null 2>&1 + then + ff_was_pinned=true + else + print_info "Firefox not pinned to favorites" + fi + else + print_err "Desktop environment not yet supported." + print_err "Unable to repin Firefox to favorites bar" + print_info "Firefox can still be repinned manually" + fi +} # check_for_ff_pin + +repin_firefox () +{ + print_info "Attempting to repin Firefox to favorites bar..." + if [ "$ff_was_pinned" == true ] + then + # TODO: finish this + + curr_favorites=$(gsettings get org.gnome.shell favorite-apps) + print_info "Repinning Firefox to favorites bar" + + gsettings set org.gnome.shell favorite-apps "$(gsettings get org.gnome.shell favorite-apps | sed s/.$//), 'firefox.desktop']" + + print_info "Done." + fi +} # repin_firefox + +check_distro() { + if [ -f /etc/os-release ]; then + # Check if the distribution is Debian-based + if grep -q -i "debian" /etc/os-release || \ + grep -q -i "ubuntu" /etc/os-release || \ + grep -q -i "pop" /etc/os-release || \ + grep -q -i "mint" /etc/os-release; then + distro="debian" + # Check if the distribution is Arch Linux + elif grep -q -i "arch" /etc/os-release; then + distro="arch" + else + distro="other" + fi + else + distro="unknown" + fi +} + +setup_debian() { + mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash\|snap") + # Check if databases were found properly + if [ "${#databases[@]}" -eq 0 ] + then + # Database was not found + if [ "$snap_ff" == true ] + then + revert_firefox + else + # Firefox was not replaced, exit with E_DATABASE error + print_err "No valid databases located. Try running, then closing Firefox, then start this script again." + echo -e "\tExiting..." + + exit "$E_DATABASE" + fi + else + # Database was found. (Good) + if [ "$snap_ff" == true ] + then + # Database was found, meaning snap firefox was replaced with apt version + # This conditional branch may not be needed at all... Note: Remove if not needed + snap_ff=false + fi + fi + + # Install middleware and necessary utilities + print_info "Installing middleware..." + apt update + DEBIAN_FRONTEND=noninteractive apt install -y libpcsclite1 pcscd libccid libpcsc-perl pcsc-tools libnss3-tools unzip wget + print_info "Done" + + # Pull all necessary files + print_info "Downloading DoD certificates and Cackey package..." + wget -qP "$DWNLD_DIR" "$CERT_URL" + wget -qP "$DWNLD_DIR" "$CACKEY_URL" + print_info "Done." + + # Install libcackey. + if [ -e "$DWNLD_DIR/$PKG_FILENAME" ] + then + print_info "Installing libcackey..." + if dpkg -i "$DWNLD_DIR/$PKG_FILENAME" + then + print_info "Done." + else + print_err "Installation failed. Exiting..." + + exit "$E_INSTALL" + fi + fi + + # Prevent cackey from upgrading. + # If cackey upgrades beyond 7.5, it moves libcackey.so to a different location, + # breaking Firefox. Returning libcackey.so to the original location does not + # seem to fix this issue. + if apt-mark hold cackey + then + print_info "Hold placed on cackey package" + else + print_err "Failed to place hold on cackey package" + fi +} + +setup_arch() { + mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash") + # Check if databases were found properly + if [ "${#databases[@]}" -eq 0 ] + then + # Database was not found + print_err "No valid databases located. Try running, then closing Firefox, then start this script again." + echo -e "\tExiting..." + + exit "$E_DATABASE" + fi + + # Install middleware and necessary utilities + print_info "Installing middleware..." + pacman -Syu --noconfirm + pacman -S --noconfirm ccid opensc pcsc-tools nss unzip wget + print_info "Done" + + # Pull all necessary files + print_info "Downloading DoD certificates and Cackey package..." + wget -qP "$DWNLD_DIR" "$CERT_URL" + wget -qP "$DWNLD_DIR" "$CACKEY_URL" + print_info "Done." + + + # Install libcackey. + if command -v yay >/dev/null 2>&1; then + print_info "Installing libcackey using yay..." + sudo -u "$SUDO_USER" yay -S --noconfirm cackey + elif command -v paru >/dev/null 2>&1; then + print_info "Installing libcackey using paru..." + sudo -u "$SUDO_USER" paru -S --noconfirm cackey + else + print_err "Neither yay nor paru is available. Please install one of them and try again." + exit 1 + fi + + # Check if libcackey.so is installed + libcackey_path=$(find /usr -name "libcackey.so" | head -n 1) + if [ -z "$libcackey_path" ]; then + print_err "libcackey.so not found after installation. Please check the installation manually." + exit 1 + else + print_info "libcackey.so found at $libcackey_path" + fi +} +main From 6f990db6ce46ced22ef480ab527f0c8913f1e72f Mon Sep 17 00:00:00 2001 From: Derrek Landauer Date: Sat, 20 Apr 2024 16:06:21 -0600 Subject: [PATCH 3/6] refactored script to help removed redudant logic and to imrove both integration common functions and separation of distro specfic tasks. --- cac_setup.sh | 655 ++++-------------------------- cac_setup_debian.sh | 490 ---------------------- sh/arch_helper.sh | 61 +++ cac_setup_arch.sh => sh/common.sh | 425 +++++++------------ sh/debian_helper.sh | 133 ++++++ 5 files changed, 419 insertions(+), 1345 deletions(-) delete mode 100755 cac_setup_debian.sh create mode 100644 sh/arch_helper.sh rename cac_setup_arch.sh => sh/common.sh (63%) mode change 100755 => 100644 create mode 100644 sh/debian_helper.sh diff --git a/cac_setup.sh b/cac_setup.sh index 4dbdc90..d534942 100755 --- a/cac_setup.sh +++ b/cac_setup.sh @@ -1,586 +1,77 @@ #!/usr/bin/env bash - # cac_setup.sh # Description: Setup a Linux environment for Common Access Card use. -main () -{ - EXIT_SUCCESS=0 # Success exit code - E_INSTALL=85 # Installation failed - E_NOTROOT=86 # Non-root exit error - E_BROWSER=87 # Compatible browser not found - E_DATABASE=88 # No database located - DWNLD_DIR="/tmp" # Reliable location to place artifacts - FF_PROFILE_NAME="old_ff_profile" # Reliable location to place artifacts - - chrome_exists=false # Google Chrome is installed - ff_exists=false # Firefox is installed - snap_ff=false # Flag to prompt for how to handle snap Firefox - - ORIG_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)" - CERT_EXTENSION="cer" - PKCS_FILENAME="pkcs11.txt" - DB_FILENAME="cert9.db" - CERT_FILENAME="AllCerts" - BUNDLE_FILENAME="AllCerts.zip" - CERT_URL="http://militarycac.com/maccerts/$BUNDLE_FILENAME" - PKG_FILENAME="cackey_0.7.5-1_amd64.deb" - CACKEY_URL="http://cackey.rkeene.org/download/0.7.5/$PKG_FILENAME" - - root_check - browser_check - - check_distro - - if [ "$distro" == "debian" ]; then - setup_debian - elif [ "$distro" == "arch" ]; then - setup_arch - else - echo "The current distribution is not supported by this script." - exit 1 - fi - - # Unzip cert bundle - if [ -e "$DWNLD_DIR/$BUNDLE_FILENAME" ] - then - mkdir -p "$DWNLD_DIR/$CERT_FILENAME" - unzip "$DWNLD_DIR/$BUNDLE_FILENAME" -d "$DWNLD_DIR/$CERT_FILENAME" - fi - - # Import certificates into cert9.db databases for browsers - for db in "${databases[@]}" - do - if [ -n "$db" ] - then - import_certs "$db" - fi - done - - print_info "Enabling pcscd service to start on boot..." - systemctl enable pcscd.socket - - # Remove artifacts - print_info "Removing artifacts..." - rm -rf "${DWNLD_DIR:?}"/{"$BUNDLE_FILENAME","$CERT_FILENAME","$PKG_FILENAME","$FF_PROFILE_NAME"} 2>/dev/null - if [ "$?" -ne "$EXIT_SUCCESS" ] - then - print_err "Failed to remove artifacts" - else - print_info "Done." - fi - - exit "$EXIT_SUCCESS" -} # main - -# Prints message with red [ERROR] tag before the message -print_err () -{ - ERR_COLOR='\033[0;31m' # Red for error messages - NO_COLOR='\033[0m' # Revert terminal back to no color - - echo -e "${ERR_COLOR}[ERROR]${NO_COLOR} $1" -} # print_err - -# Prints message with yellow [INFO] tag before the message -print_info () -{ - INFO_COLOR='\033[0;33m' # Yellow for notes - NO_COLOR='\033[0m' # Revert terminal back to no color - - echo -e "${INFO_COLOR}[INFO]${NO_COLOR} $1" -} # print_info - -# Check to ensure the script is executed as root -root_check () -{ - local ROOT_UID=0 # Only users with $UID 0 have root privileges - - # Ensure the script is ran as root - if [ "${EUID:-$(id -u)}" -ne "$ROOT_UID" ] - then - print_err "Please run this script as root." - exit "$E_NOTROOT" - fi -} # root_check - -# Replace the current snap version of Firefox with the compatible apt version of Firefox -reconfigure_firefox () -{ - # Replace snap Firefox with version from PPA maintained via Mozilla - - check_for_ff_pin - - #Profile migration - backup_ff_profile - - print_info "Removing Snap version of Firefox" - snap remove --purge firefox - - print_info "Adding PPA for Mozilla maintained Firefox" - add-apt-repository -y ppa:mozillateam/ppa - - print_info "Setting priority to prefer Mozilla PPA over snap package" - echo -e "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox - - print_info "Enabling updates for future Firefox releases" - # shellcheck disable=SC2016 - echo -e 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' > /etc/apt/apt.conf.d/51unattended-upgrades-firefox - - print_info "Installing Firefox via apt" - apt install firefox -y - print_info "Completed re-installation of Firefox" - - # Forget the previous location of firefox executable - if hash firefox - then - hash -d firefox - fi - - run_firefox - - print_info "Finished, closing Firefox." - - if [ "$backup_exists" == true ] - then - print_info "Migrating user profile into newly installed Firefox" - migrate_ff_profile "migrate" - fi - - repin_firefox - -} # reconfigure_firefox - -run_firefox () -{ - print_info "Starting Firefox silently to complete post-install actions..." - sudo -H -u "$SUDO_USER" firefox --headless --first-startup >/dev/null 2>&1 & - sleep 3 - pkill -9 firefox - sleep 1 -} - -# Discovery of browsers installed on the user's system -# Sets appropriate flags to control the flow of the installation, depending on -# what is needed for the individual user -browser_check () -{ - print_info "Checking for Firefox and Chrome..." - check_for_firefox - - if [ "$distro" == "debian" ]; then - check_for_chrome_debian - elif [ "$distro" == "arch" ]; then - check_for_chrome_arch - fi - - # Browser check results - if [ "$ff_exists" == false ] && [ "$chrome_exists" == false ] - then - print_err "No version of Mozilla Firefox OR Google Chrome has been detected." - print_info "Please install either or both to proceed." - - exit "$E_BROWSER" - - elif [ "$ff_exists" == true ] # Firefox was found - then - if [ "$snap_ff" == true ] # Snap version of Firefox - then - echo -e " - ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}******************** - * The version of Firefox you have installed * - * currently was installed via snap. * - * This version of Firefox is not currently * - * compatible with the method used to enable CAC * - * support in browsers. * - * * - * As a work-around, this script can automatically * - * remove the snap version and reinstall via apt. * - * * - * The option to attempt to migrate all of your * - * personalizations will be given if you choose to * - * replace Firefox via this script. Your Firefox * - * profile will be saved to a temp location, then * - * will overwrite the default profile once the apt * - * version of Firefox has been installed. * - * * - ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}********************\n" - - # Prompt user to elect to replace snap firefox with apt firefox - choice='' - while [ "$choice" != "y" ] && [ "$choice" != "n" ] - do - echo -e "\nWould you like to switch to the apt version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" - read -rp '> ' choice - done - - if [ "$choice" == "y" ] - then - reconfigure_firefox - else - if [ $chrome_exists == false ] - then - print_info "You have elected to keep the snap version of Firefox.\n" - print_err "You have no compatible browsers. Exiting..." - - exit $E_BROWSER - fi - fi - fi - fi -} - -# Locate and backup the profile for the user's snap version of Firefox -# Backup is placed in /tmp/ff_old_profile/ and can be restored after the -# apt version of Firefox has been installed -backup_ff_profile () -{ - location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" - if [ -z "$location" ] - then - print_info "No user profile was found in snap-installed version of Firefox." - else - # A user profile exists in the snap version of FF - choice='' - while [ "$choice" != "y" ] && [ "$choice" != "n" ] - do - echo -e "\nWould you like to transfer your bookmarks and personalizations to the new version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" - read -rp '> ' choice - done - - if [ "$choice" == "y" ] - then - print_info "Backing up Firefox profile" - ff_profile="$(dirname "$location")" - sudo -H -u "$SUDO_USER" cp -rf "$ff_profile" "$DWNLD_DIR/$FF_PROFILE_NAME" - backup_exists=1 - fi - - fi -} # backup_ff_profile - -# Moves the user's backed up Firefox profile from the temp location to the newly -# installed apt version of Firefox in the ~/.mozilla directory -# TODO: Take arguments for source and destination so profile can be restored to -# original location in the event of a failed install -migrate_ff_profile () -{ - direction=$1 - - if [ "$direction" == "migrate" ] - then - apt_ff_profile="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep -v snap)" - if [ -z "$apt_ff_profile" ] - then - print_err "Something went wrong while trying to find apt Firefox's user profile directory." - exit "$E_DATABASE" - else - ff_profile_dir="$(dirname "$apt_ff_profile")" - if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" - then - print_info "Successfully migrated user profile for Firefox versions" - else - print_err "Unable to migrate Firefox profile" - fi - fi - elif [ "$direction" == "restore" ] - then - location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" - if [ -z "$location" ] - then - print_info "No user profile was found in snap-installed version of Firefox." - else - ff_profile_dir="$(dirname "$apt_ff_profile")" - if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" - then - print_info "Successfully restored user profile for Firefox" - else - print_err "Unable to migrate Firefox profile" - fi - fi - fi - -} - -# Attempt to find an installed version of Firefox on the user's system -# Determines whether the version is installed via snap or apt -check_for_firefox () -{ - if command -v firefox >/dev/null - then - ff_exists=true - print_info "Found Firefox." - if command -v firefox | grep snap >/dev/null - then - snap_ff=true - print_err "This version of Firefox was installed as a snap package" - else - # Run Firefox to ensure .mozilla directory has been created - echo -e "Running Firefox to generate profile directory..." - sudo -H -u "$SUDO_USER" bash -c 'firefox --headless --first-startup >/dev/null 2>&1 &' - sleep 3 - pkill -9 firefox - sleep 1 - echo -e "\tDone." - fi - else - print_info "Firefox not found." - fi -} -# Attempt to find a version of Google Chrome installed on the user's system -check_for_chrome_debian () -{ - # Check to see if Chrome exists - if command -v google-chrome >/dev/null - then - chrome_exists=true - print_info "Found Google Chrome." - # Run Chrome to ensure .pki directory has been created -# echo -e "\tRunning Chrome to ensure it has completed post-install actions..." -# # TODO: finish troubleshooting this -# sudo -H -u "$SUDO_USER" bash -c 'google-chrome --headless --disable-gpu >/dev/null 2>&1 &' -# sleep 3 -# pkill -9 google-chrome -# sleep 1 -# echo -e "\tDone." - else - print_info "Chrome not found." - fi -} - - -check_for_chrome_arch () -{ - # Check to see if Chrome exists - if command -v google-chrome-stable >/dev/null - then - chrome_exists=true - print_info "Found Google Chrome." - else - print_info "Chrome not found." - fi -} - -# Re-install the user's previous version of Firefox if the snap version was -# removed in the process of this script. -revert_firefox () -{ - # Firefox was replaced, lets put it back where it was. - print_err "No valid databases located. Reinstalling previous version of Firefox..." - apt purge firefox -y - snap install firefox - - run_firefox - - print_info "Completed. Exiting..." - - # "Restore" old profile back to the snap version of Firefox - migrate_ff_profile "restore" - - exit "$E_DATABASE" -} - -# Integrate all certificates into the databases for existing browsers - import_certs () -{ - db=$1 - db_root="$(dirname "$db")" - if [ -n "$db_root" ] - then - case "$db_root" in - *"pki"*) - print_info "Importing certificates for Chrome..." - echo - ;; - *"firefox"*) - print_info "Importing certificates for Firefox..." - echo - ;; - esac - - print_info "Loading certificates into $db_root " - echo - - for cert in "$DWNLD_DIR/$CERT_FILENAME/"*."$CERT_EXTENSION" - do - echo "Importing $cert" - certutil -d sql:"$db_root" -A -t TC -n "$cert" -i "$cert" - done - - if ! grep -Pzo 'library=/usr/lib64/libcackey.so\nname=CAC Module\n' "$db_root/$PKCS_FILENAME" >/dev/null - then - printf "library=/usr/lib64/libcackey.so\nname=CAC Module\n" >> "$db_root/$PKCS_FILENAME" - fi - fi - - echo "Done." - echo -} # import_certs - -# Check to see if the user has Firefox pinned to their favorites bar in GNOME -check_for_ff_pin () -{ - # firefox is a favorite and if gnome is the desktop environment. - - if sudo -u "$SUDO_USER" bash -c "$(echo "$XDG_CURRENT_DESKTOP" | grep "GNOME" >/dev/null 2>&1)" - then - print_info "Detected Gnome desktop environment" - if echo "$curr_favorites" | grep "firefox.desktop" >/dev/null 2>&1 - then - ff_was_pinned=true - else - print_info "Firefox not pinned to favorites" - fi - else - print_err "Desktop environment not yet supported." - print_err "Unable to repin Firefox to favorites bar" - print_info "Firefox can still be repinned manually" - fi -} # check_for_ff_pin - -repin_firefox () -{ - print_info "Attempting to repin Firefox to favorites bar..." - if [ "$ff_was_pinned" == true ] - then - # TODO: finish this - - curr_favorites=$(gsettings get org.gnome.shell favorite-apps) - print_info "Repinning Firefox to favorites bar" - - gsettings set org.gnome.shell favorite-apps "$(gsettings get org.gnome.shell favorite-apps | sed s/.$//), 'firefox.desktop']" - - print_info "Done." - fi -} # repin_firefox - -check_distro() { - if [ -f /etc/os-release ]; then - # Check if the distribution is Debian-based - if grep -q -i "debian" /etc/os-release || \ - grep -q -i "ubuntu" /etc/os-release || \ - grep -q -i "pop" /etc/os-release || \ - grep -q -i "mint" /etc/os-release; then - distro="debian" - # Check if the distribution is Arch Linux - elif grep -q -i "arch" /etc/os-release; then - distro="arch" - else - distro="other" - fi - else - distro="unknown" - fi -} - -setup_debian() { - mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash\|snap") - # Check if databases were found properly - if [ "${#databases[@]}" -eq 0 ] - then - # Database was not found - if [ "$snap_ff" == true ] - then - revert_firefox - else - # Firefox was not replaced, exit with E_DATABASE error - print_err "No valid databases located. Try running, then closing Firefox, then start this script again." - echo -e "\tExiting..." - - exit "$E_DATABASE" - fi - else - # Database was found. (Good) - if [ "$snap_ff" == true ] - then - # Database was found, meaning snap firefox was replaced with apt version - # This conditional branch may not be needed at all... Note: Remove if not needed - snap_ff=false - fi - fi - - # Install middleware and necessary utilities - print_info "Installing middleware..." - apt update - DEBIAN_FRONTEND=noninteractive apt install -y libpcsclite1 pcscd libccid libpcsc-perl pcsc-tools libnss3-tools unzip wget - print_info "Done" - - # Pull all necessary files - print_info "Downloading DoD certificates and Cackey package..." - wget -qP "$DWNLD_DIR" "$CERT_URL" - wget -qP "$DWNLD_DIR" "$CACKEY_URL" - print_info "Done." - - # Install libcackey. - if [ -e "$DWNLD_DIR/$PKG_FILENAME" ] - then - print_info "Installing libcackey..." - if dpkg -i "$DWNLD_DIR/$PKG_FILENAME" - then - print_info "Done." - else - print_err "Installation failed. Exiting..." - - exit "$E_INSTALL" - fi - fi - - # Prevent cackey from upgrading. - # If cackey upgrades beyond 7.5, it moves libcackey.so to a different location, - # breaking Firefox. Returning libcackey.so to the original location does not - # seem to fix this issue. - if apt-mark hold cackey - then - print_info "Hold placed on cackey package" - else - print_err "Failed to place hold on cackey package" - fi -} - -setup_arch() { - mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash") - # Check if databases were found properly - if [ "${#databases[@]}" -eq 0 ] - then - # Database was not found - print_err "No valid databases located. Try running, then closing Firefox, then start this script again." - echo -e "\tExiting..." - - exit "$E_DATABASE" - fi - - # Install middleware and necessary utilities - print_info "Installing middleware..." - pacman -Syu --noconfirm - pacman -S --noconfirm ccid opensc pcsc-tools nss unzip wget - print_info "Done" - - # Pull all necessary files - print_info "Downloading DoD certificates and Cackey package..." - wget -qP "$DWNLD_DIR" "$CERT_URL" - wget -qP "$DWNLD_DIR" "$CACKEY_URL" - print_info "Done." - - - # Install libcackey. - if command -v yay >/dev/null 2>&1; then - print_info "Installing libcackey using yay..." - sudo -u "$SUDO_USER" yay -S --noconfirm cackey - elif command -v paru >/dev/null 2>&1; then - print_info "Installing libcackey using paru..." - sudo -u "$SUDO_USER" paru -S --noconfirm cackey - else - print_err "Neither yay nor paru is available. Please install one of them and try again." - exit 1 - fi - - # Check if libcackey.so is installed - libcackey_path=$(find /usr -name "libcackey.so" | head -n 1) - if [ -z "$libcackey_path" ]; then - print_err "libcackey.so not found after installation. Please check the installation manually." - exit 1 - else - print_info "libcackey.so found at $libcackey_path" - fi -} -main +CUR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +ORIG_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)" + +EXIT_SUCCESS=0 # Success exit code +E_INSTALL=85 # Installation failed +E_NOTROOT=86 # Non-root exit error +E_BROWSER=87 # Compatible browser not found +E_DATABASE=88 # No database located +DWNLD_DIR="/tmp" # Reliable location to place artifacts +FF_PROFILE_NAME="old_ff_profile" # Reliable location to place artifacts + +chrome_exists=false # Google Chrome is installed +ff_exists=false # Firefox is installed +snap_ff=false # Flag to prompt for how to handle snap Firefox + +CERT_EXTENSION="cer" +PKCS_FILENAME="pkcs11.txt" +DB_FILENAME="cert9.db" +CERT_FILENAME="AllCerts" +BUNDLE_FILENAME="AllCerts.zip" +CERT_URL="http://mplay nicely with libcacilitarycac.com/maccerts/$BUNDLE_FILENAME" +PKG_FILENAME="cackey_0.7.5-1_amd64.deb" +CACKEY_URL="http://cackey.rkeene.org/download/0.7.5/$PKG_FILENAME" + +# source common functions +source "$CUR_DIR"/sh/common.sh + +# check if distribution is debian-based or arch +check_distro + +# Determine the Linux distribution by checking the release information +if [[ $distro == "debian" ]]; then + # Source the Debian-specific helper script if Debian is detected + echo "Debian-based distribution detected." + source "$CUR_DIR"/sh/debian_helper.sh +elif [[ $distro == "arch" ]]; then + # Source the Arch-specific helper script if Arch Linux is detected + echo "Arch Linux detected." + source "$CUR_DIR"/sh/arch_helper.sh +else + echo "The current distribution is not supported by this script." + exit 1 +fi + +# Check if the script is executed as root +root_check + +# Check if Firefox/Chrome browsers are installed +browser_check + +# Call distro specific sourced setup function +setup + +# Unzip and import the certificate bundle +unzip_cert_bundle + +# Import certificates into cert9.db databases for browsers +for db in "${databases[@]}" +do + if [ -n "$db" ]; then + import_certs "$db" + fi +done + +# enable pcscd service +print_info "Enabling pcscd service to start on boot..." +systemctl enable pcscd.socket + +# Remove installation artifacts +remove_artifacts + +# Exit the script +exit "$EXIT_SUCCESS" diff --git a/cac_setup_debian.sh b/cac_setup_debian.sh deleted file mode 100755 index 3a5f78c..0000000 --- a/cac_setup_debian.sh +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env bash - -# cac_setup.sh -# Description: Setup a Linux environment for Common Access Card use. - -main () -{ - EXIT_SUCCESS=0 # Success exit code - E_INSTALL=85 # Installation failed - E_NOTROOT=86 # Non-root exit error - E_BROWSER=87 # Compatible browser not found - E_DATABASE=88 # No database located - DWNLD_DIR="/tmp" # Reliable location to place artifacts - FF_PROFILE_NAME="old_ff_profile" # Reliable location to place artifacts - - chrome_exists=false # Google Chrome is installed - ff_exists=false # Firefox is installed - snap_ff=false # Flag to prompt for how to handle snap Firefox - - ORIG_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)" - CERT_EXTENSION="cer" - PKCS_FILENAME="pkcs11.txt" - DB_FILENAME="cert9.db" - CERT_FILENAME="AllCerts" - BUNDLE_FILENAME="AllCerts.zip" - CERT_URL="http://militarycac.com/maccerts/$BUNDLE_FILENAME" - PKG_FILENAME="cackey_0.7.5-1_amd64.deb" - CACKEY_URL="http://cackey.rkeene.org/download/0.7.5/$PKG_FILENAME" - - root_check - browser_check - - mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash\|snap") - # Check if databases were found properly - if [ "${#databases[@]}" -eq 0 ] - then - # Database was not found - if [ "$snap_ff" == true ] - then - revert_firefox - else - # Firefox was not replaced, exit with E_DATABASE error - print_err "No valid databases located. Try running, then closing Firefox, then start this script again." - echo -e "\tExiting..." - - exit "$E_DATABASE" - fi - else - # Database was found. (Good) - if [ "$snap_ff" == true ] - then - # Database was found, meaning snap firefox was replaced with apt version - # This conditional branch may not be needed at all... Note: Remove if not needed - snap_ff=false - fi - fi - - # Install middleware and necessary utilities - print_info "Installing middleware..." - apt update - DEBIAN_FRONTEND=noninteractive apt install -y libpcsclite1 pcscd libccid libpcsc-perl pcsc-tools libnss3-tools unzip wget - print_info "Done" - - # Pull all necessary files - print_info "Downloading DoD certificates and Cackey package..." - wget -qP "$DWNLD_DIR" "$CERT_URL" - wget -qP "$DWNLD_DIR" "$CACKEY_URL" - print_info "Done." - - # Install libcackey. - if [ -e "$DWNLD_DIR/$PKG_FILENAME" ] - then - print_info "Installing libcackey..." - if dpkg -i "$DWNLD_DIR/$PKG_FILENAME" - then - print_info "Done." - else - print_err "Installation failed. Exiting..." - - exit "$E_INSTALL" - fi - fi - - # Prevent cackey from upgrading. - # If cackey upgrades beyond 7.5, it moves libcackey.so to a different location, - # breaking Firefox. Returning libcackey.so to the original location does not - # seem to fix this issue. - if apt-mark hold cackey - then - print_info "Hold placed on cackey package" - else - print_err "Failed to place hold on cackey package" - fi - - # Unzip cert bundle - if [ -e "$DWNLD_DIR/$BUNDLE_FILENAME" ] - then - mkdir -p "$DWNLD_DIR/$CERT_FILENAME" - unzip "$DWNLD_DIR/$BUNDLE_FILENAME" -d "$DWNLD_DIR/$CERT_FILENAME" - fi - - # Import certificates into cert9.db databases for browsers - for db in "${databases[@]}" - do - if [ -n "$db" ] - then - import_certs "$db" - fi - done - - print_info "Enabling pcscd service to start on boot..." - systemctl enable pcscd.socket - - # Remove artifacts - print_info "Removing artifacts..." - rm -rf "${DWNLD_DIR:?}"/{"$BUNDLE_FILENAME","$CERT_FILENAME","$PKG_FILENAME","$FF_PROFILE_NAME"} 2>/dev/null - if [ "$?" -ne "$EXIT_SUCCESS" ] - then - print_err "Failed to remove artifacts" - else - print_info "Done." - fi - - exit "$EXIT_SUCCESS" -} # main - -# Prints message with red [ERROR] tag before the message -print_err () -{ - ERR_COLOR='\033[0;31m' # Red for error messages - NO_COLOR='\033[0m' # Revert terminal back to no color - - echo -e "${ERR_COLOR}[ERROR]${NO_COLOR} $1" -} # print_err - -# Prints message with yellow [INFO] tag before the message -print_info () -{ - INFO_COLOR='\033[0;33m' # Yellow for notes - NO_COLOR='\033[0m' # Revert terminal back to no color - - echo -e "${INFO_COLOR}[INFO]${NO_COLOR} $1" -} # print_info - -# Check to ensure the script is executed as root -root_check () -{ - local ROOT_UID=0 # Only users with $UID 0 have root privileges - - # Ensure the script is ran as root - if [ "${EUID:-$(id -u)}" -ne "$ROOT_UID" ] - then - print_err "Please run this script as root." - exit "$E_NOTROOT" - fi -} # root_check - -# Replace the current snap version of Firefox with the compatible apt version of Firefox -reconfigure_firefox () -{ - # Replace snap Firefox with version from PPA maintained via Mozilla - - check_for_ff_pin - - #Profile migration - backup_ff_profile - - print_info "Removing Snap version of Firefox" - snap remove --purge firefox - - print_info "Adding PPA for Mozilla maintained Firefox" - add-apt-repository -y ppa:mozillateam/ppa - - print_info "Setting priority to prefer Mozilla PPA over snap package" - echo -e "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox - - print_info "Enabling updates for future Firefox releases" - # shellcheck disable=SC2016 - echo -e 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' > /etc/apt/apt.conf.d/51unattended-upgrades-firefox - - print_info "Installing Firefox via apt" - apt install firefox -y - print_info "Completed re-installation of Firefox" - - # Forget the previous location of firefox executable - if hash firefox - then - hash -d firefox - fi - - run_firefox - - print_info "Finished, closing Firefox." - - if [ "$backup_exists" == true ] - then - print_info "Migrating user profile into newly installed Firefox" - migrate_ff_profile "migrate" - fi - - repin_firefox - -} # reconfigure_firefox - -run_firefox () -{ - print_info "Starting Firefox silently to complete post-install actions..." - sudo -H -u "$SUDO_USER" firefox --headless --first-startup >/dev/null 2>&1 & - sleep 3 - pkill -9 firefox - sleep 1 -} - -# Discovery of browsers installed on the user's system -# Sets appropriate flags to control the flow of the installation, depending on -# what is needed for the individual user -browser_check () -{ - print_info "Checking for Firefox and Chrome..." - check_for_firefox - check_for_chrome - - # Browser check results - if [ "$ff_exists" == false ] && [ "$chrome_exists" == false ] - then - print_err "No version of Mozilla Firefox OR Google Chrome has been detected." - print_info "Please install either or both to proceed." - - exit "$E_BROWSER" - - elif [ "$ff_exists" == true ] # Firefox was found - then - if [ "$snap_ff" == true ] # Snap version of Firefox - then - echo -e " - ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}******************** - * The version of Firefox you have installed * - * currently was installed via snap. * - * This version of Firefox is not currently * - * compatible with the method used to enable CAC * - * support in browsers. * - * * - * As a work-around, this script can automatically * - * remove the snap version and reinstall via apt. * - * * - * The option to attempt to migrate all of your * - * personalizations will be given if you choose to * - * replace Firefox via this script. Your Firefox * - * profile will be saved to a temp location, then * - * will overwrite the default profile once the apt * - * version of Firefox has been installed. * - * * - ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}********************\n" - - # Prompt user to elect to replace snap firefox with apt firefox - choice='' - while [ "$choice" != "y" ] && [ "$choice" != "n" ] - do - echo -e "\nWould you like to switch to the apt version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" - read -rp '> ' choice - done - - if [ "$choice" == "y" ] - then - reconfigure_firefox - else - if [ $chrome_exists == false ] - then - print_info "You have elected to keep the snap version of Firefox.\n" - print_err "You have no compatible browsers. Exiting..." - - exit $E_BROWSER - fi - fi - fi - fi -} - -# Locate and backup the profile for the user's snap version of Firefox -# Backup is placed in /tmp/ff_old_profile/ and can be restored after the -# apt version of Firefox has been installed -backup_ff_profile () -{ - location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" - if [ -z "$location" ] - then - print_info "No user profile was found in snap-installed version of Firefox." - else - # A user profile exists in the snap version of FF - choice='' - while [ "$choice" != "y" ] && [ "$choice" != "n" ] - do - echo -e "\nWould you like to transfer your bookmarks and personalizations to the new version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" - read -rp '> ' choice - done - - if [ "$choice" == "y" ] - then - print_info "Backing up Firefox profile" - ff_profile="$(dirname "$location")" - sudo -H -u "$SUDO_USER" cp -rf "$ff_profile" "$DWNLD_DIR/$FF_PROFILE_NAME" - backup_exists=1 - fi - - fi -} # backup_ff_profile - -# Moves the user's backed up Firefox profile from the temp location to the newly -# installed apt version of Firefox in the ~/.mozilla directory -# TODO: Take arguments for source and destination so profile can be restored to -# original location in the event of a failed install -migrate_ff_profile () -{ - direction=$1 - - if [ "$direction" == "migrate" ] - then - apt_ff_profile="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep -v snap)" - if [ -z "$apt_ff_profile" ] - then - print_err "Something went wrong while trying to find apt Firefox's user profile directory." - exit "$E_DATABASE" - else - ff_profile_dir="$(dirname "$apt_ff_profile")" - if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" - then - print_info "Successfully migrated user profile for Firefox versions" - else - print_err "Unable to migrate Firefox profile" - fi - fi - elif [ "$direction" == "restore" ] - then - location="$(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox" | grep -v "Trash" | grep snap)" - if [ -z "$location" ] - then - print_info "No user profile was found in snap-installed version of Firefox." - else - ff_profile_dir="$(dirname "$apt_ff_profile")" - if sudo -H -u "$SUDO_USER" cp -rf "$DWNLD_DIR/$FF_PROFILE_NAME"/* "$ff_profile_dir" - then - print_info "Successfully restored user profile for Firefox" - else - print_err "Unable to migrate Firefox profile" - fi - fi - fi - -} - -# Attempt to find an installed version of Firefox on the user's system -# Determines whether the version is installed via snap or apt -check_for_firefox () -{ - if command -v firefox >/dev/null - then - ff_exists=true - print_info "Found Firefox." - if command -v firefox | grep snap >/dev/null - then - snap_ff=true - print_err "This version of Firefox was installed as a snap package" - else - # Run Firefox to ensure .mozilla directory has been created - echo -e "Running Firefox to generate profile directory..." - sudo -H -u "$SUDO_USER" bash -c 'firefox --headless --first-startup >/dev/null 2>&1 &' - sleep 3 - pkill -9 firefox - sleep 1 - echo -e "\tDone." - fi - else - print_info "Firefox not found." - fi -} -# Attempt to find a version of Google Chrome installed on the user's system -check_for_chrome () -{ - # Check to see if Chrome exists - if command -v google-chrome >/dev/null - then - chrome_exists=true - print_info "Found Google Chrome." - # Run Chrome to ensure .pki directory has been created -# echo -e "\tRunning Chrome to ensure it has completed post-install actions..." -# # TODO: finish troubleshooting this -# sudo -H -u "$SUDO_USER" bash -c 'google-chrome --headless --disable-gpu >/dev/null 2>&1 &' -# sleep 3 -# pkill -9 google-chrome -# sleep 1 -# echo -e "\tDone." - else - print_info "Chrome not found." - fi -} - -# Re-install the user's previous version of Firefox if the snap version was -# removed in the process of this script. -revert_firefox () -{ - # Firefox was replaced, lets put it back where it was. - print_err "No valid databases located. Reinstalling previous version of Firefox..." - apt purge firefox -y - snap install firefox - - run_firefox - - print_info "Completed. Exiting..." - - # "Restore" old profile back to the snap version of Firefox - migrate_ff_profile "restore" - - exit "$E_DATABASE" -} - -# Integrate all certificates into the databases for existing browsers - import_certs () -{ - db=$1 - db_root="$(dirname "$db")" - if [ -n "$db_root" ] - then - case "$db_root" in - *"pki"*) - print_info "Importing certificates for Chrome..." - echo - ;; - *"firefox"*) - print_info "Importing certificates for Firefox..." - echo - ;; - esac - - print_info "Loading certificates into $db_root " - echo - - for cert in "$DWNLD_DIR/$CERT_FILENAME/"*."$CERT_EXTENSION" - do - echo "Importing $cert" - certutil -d sql:"$db_root" -A -t TC -n "$cert" -i "$cert" - done - - if ! grep -Pzo 'library=/usr/lib64/libcackey.so\nname=CAC Module\n' "$db_root/$PKCS_FILENAME" >/dev/null - then - printf "library=/usr/lib64/libcackey.so\nname=CAC Module\n" >> "$db_root/$PKCS_FILENAME" - fi - fi - - echo "Done." - echo -} # import_certs - -# Check to see if the user has Firefox pinned to their favorites bar in GNOME -check_for_ff_pin () -{ - # firefox is a favorite and if gnome is the desktop environment. - - if sudo -u "$SUDO_USER" bash -c "$(echo "$XDG_CURRENT_DESKTOP" | grep "GNOME" >/dev/null 2>&1)" - then - print_info "Detected Gnome desktop environment" - if echo "$curr_favorites" | grep "firefox.desktop" >/dev/null 2>&1 - then - ff_was_pinned=true - else - print_info "Firefox not pinned to favorites" - fi - else - print_err "Desktop environment not yet supported." - print_err "Unable to repin Firefox to favorites bar" - print_info "Firefox can still be repinned manually" - fi -} # check_for_ff_pin - -repin_firefox () -{ - print_info "Attempting to repin Firefox to favorites bar..." - if [ "$ff_was_pinned" == true ] - then - # TODO: finish this - - curr_favorites=$(gsettings get org.gnome.shell favorite-apps) - print_info "Repinning Firefox to favorites bar" - - gsettings set org.gnome.shell favorite-apps "$(gsettings get org.gnome.shell favorite-apps | sed s/.$//), 'firefox.desktop']" - - print_info "Done." - fi -} # repin_firefox - -main diff --git a/sh/arch_helper.sh b/sh/arch_helper.sh new file mode 100644 index 0000000..4fb9e87 --- /dev/null +++ b/sh/arch_helper.sh @@ -0,0 +1,61 @@ +check_for_chrome() +{ + # Check to see if Chrome exists + if command -v google-chrome-stable >/dev/null + then + chrome_exists=true + print_info "Found Google Chrome." + else + print_info "Chrome not found." + fi +} + + +setup() +{ + # Find all Firefox databases + mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash") + # Check if databases were found properly + if [ "${#databases[@]}" -eq 0 ] + then + # Database was not found + print_err "No valid databases located. Try running, then closing Firefox, then start this script again." + echo -e "\tExiting..." + + exit "$E_DATABASE" + fi + + # Install middleware and necessary utilities + print_info "Installing middleware..." + pacman -Sy --noconfirm # Update package list + pacman -S --noconfirm ccid opensc pcsc-tools nss unzip wget + print_info "Done" + + # Pull all necessary files + print_info "Downloading DoD certificates and Cackey package..." + wget -qP "$DWNLD_DIR" "$CERT_URL" + wget -qP "$DWNLD_DIR" "$CACKEY_URL" + print_info "Done." + + + # Install libcackey. + if command -v yay >/dev/null 2>&1; then + print_info "Installing libcackey using yay..." + sudo -u "$SUDO_USER" yay -S --noconfirm cackey + elif command -v paru >/dev/null 2>&1; then + print_info "Installing libcackey using paru..." + sudo -u "$SUDO_USER" paru -S --noconfirm cackey + else + print_err "Neither yay nor paru is available. Please install one of them and try again." + exit 1 + fi + + # Check if libcackey.so is installed + libcackey_path=$(find /usr -name "libcackey.so" | head -n 1) + if [ -z "$libcackey_path" ]; then + print_err "libcackey.so not found after installation. Please check the installation manually." + exit 1 + else + print_info "libcackey.so found at $libcackey_path" + fi +} diff --git a/cac_setup_arch.sh b/sh/common.sh old mode 100755 new mode 100644 similarity index 63% rename from cac_setup_arch.sh rename to sh/common.sh index 7e481f0..874ff22 --- a/cac_setup_arch.sh +++ b/sh/common.sh @@ -1,128 +1,29 @@ -#!/usr/bin/env bash - -# cac_setup.sh -# Description: Setup a Linux environment for Common Access Card use. - -main () +check_distro() { - EXIT_SUCCESS=0 # Success exit code - E_INSTALL=85 # Installation failed - E_NOTROOT=86 # Non-root exit error - E_BROWSER=87 # Compatible browser not found - E_DATABASE=88 # No database located - DWNLD_DIR="/tmp" # Reliable location to place artifacts - FF_PROFILE_NAME="old_ff_profile" # Reliable location to place artifacts - - chrome_exists=false # Google Chrome is installed - ff_exists=false # Firefox is installed - - ORIG_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)" - CERT_EXTENSION="cer" - PKCS_FILENAME="pkcs11.txt" - DB_FILENAME="cert9.db" - CERT_FILENAME="AllCerts" - BUNDLE_FILENAME="AllCerts.zip" - CERT_URL="http://militarycac.com/maccerts/$BUNDLE_FILENAME" - PKG_FILENAME="cackey_0.7.5-1_amd64.pkg.tar.xz" - CACKEY_URL="https://aur.archlinux.org/cgit/aur.git/snapshot/cackey.tar.gz" - - root_check - browser_check - - mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash") - # Check if databases were found properly - if [ "${#databases[@]}" -eq 0 ] - then - # Database was not found - print_err "No valid databases located. Try running, then closing Firefox, then start this script again." - echo -e "\tExiting..." - - exit "$E_DATABASE" + # Set the distro to unknown by default + distro="unknown" + # Determine the Linux distribution by checking the release information + if [ -f /etc/os-release ]; then + # Define an associative array with distros and their types + declare -A distro_checks=( ["debian"]="debian ubuntu pop mint" ["arch"]="arch" ) + + # Loop through the distro types and their corresponding keywords + for type in "${!distro_checks[@]}"; do + for keyword in ${distro_checks[$type]}; do + if grep -q -i "$keyword" /etc/os-release; then + distro=$type + break 2 # Break out of both loops once a match is found + fi + done + done fi -# Install middleware and necessary utilities -print_info "Installing middleware..." -pacman -Syu --noconfirm -pacman -S --noconfirm ccid opensc pcsc-tools nss unzip wget -print_info "Done" - -# Pull all necessary files -print_info "Downloading DoD certificates and Cackey package..." -wget -qP "$DWNLD_DIR" "$CERT_URL" -wget -qP "$DWNLD_DIR" "$CACKEY_URL" -print_info "Done." - - -# Install libcackey. -if command -v yay >/dev/null 2>&1; then - print_info "Installing libcackey using yay..." - sudo -u "$SUDO_USER" yay -S --noconfirm cackey -elif command -v paru >/dev/null 2>&1; then - print_info "Installing libcackey using paru..." - sudo -u "$SUDO_USER" paru -S --noconfirm cackey -else - print_err "Neither yay nor paru is available. Please install one of them and try again." - exit 1 -fi - -# Check if libcackey.so is installed -libcackey_path=$(find /usr -name "libcackey.so" | head -n 1) -if [ -z "$libcackey_path" ]; then - print_err "libcackey.so not found after installation. Please check the installation manually." - exit 1 -else - print_info "libcackey.so found at $libcackey_path" -fi - -# Unzip cert bundle -if [ -e "$DWNLD_DIR/$BUNDLE_FILENAME" ] -then - mkdir -p "$DWNLD_DIR/$CERT_FILENAME" - unzip "$DWNLD_DIR/$BUNDLE_FILENAME" -d "$DWNLD_DIR/$CERT_FILENAME" -fi - -# Import certificates into cert9.db databases for browsers -for db in "${databases[@]}" -do - if [ -n "$db" ] - then - import_certs "$db" + if [[ $distro == "unknown" ]]; then + echo "The current distribution is not supported by this script." + exit 1 fi -done - -print_info "Enabling pcscd service to start on boot..." -systemctl enable pcscd.socket - -# Remove artifacts -print_info "Removing artifacts..." -rm -rf "${DWNLD_DIR:?}"/{"$BUNDLE_FILENAME","$CERT_FILENAME","$PKG_FILENAME","$FF_PROFILE_NAME"} 2>/dev/null -if [ "$?" -ne "$EXIT_SUCCESS" ] -then - print_err "Failed to remove artifacts" -else - print_info "Done." -fi - -exit "$EXIT_SUCCESS" -} # main - -# Prints message with red [ERROR] tag before the message -print_err () -{ - ERR_COLOR='\033[0;31m' # Red for error messages - NO_COLOR='\033[0m' # Revert terminal back to no color - - echo -e "${ERR_COLOR}[ERROR]${NO_COLOR} $1" -} # print_err - -# Prints message with yellow [INFO] tag before the message -print_info () -{ - INFO_COLOR='\033[0;33m' # Yellow for notes - NO_COLOR='\033[0m' # Revert terminal back to no color +} - echo -e "${INFO_COLOR}[INFO]${NO_COLOR} $1" -} # print_info # Check to ensure the script is executed as root root_check () @@ -137,126 +38,61 @@ root_check () fi } # root_check -# Replace the current snap version of Firefox with the compatible apt version of Firefox -reconfigure_firefox () +# Integrate all certificates into the databases for existing browsers +import_certs () { - # Replace snap Firefox with version from PPA maintained via Mozilla - - check_for_ff_pin - - #Profile migration - backup_ff_profile - - print_info "Removing Snap version of Firefox" - snap remove --purge firefox - - print_info "Adding PPA for Mozilla maintained Firefox" - add-apt-repository -y ppa:mozillateam/ppa - - print_info "Setting priority to prefer Mozilla PPA over snap package" - echo -e "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox - - print_info "Enabling updates for future Firefox releases" - # shellcheck disable=SC2016 - echo -e 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' > /etc/apt/apt.conf.d/51unattended-upgrades-firefox - - print_info "Installing Firefox via apt" - apt install firefox -y - print_info "Completed re-installation of Firefox" - - # Forget the previous location of firefox executable - if hash firefox + db=$1 + db_root="$(dirname "$db")" + if [ -n "$db_root" ] then - hash -d firefox - fi + case "$db_root" in + *"pki"*) + print_info "Importing certificates for Chrome..." + echo + ;; + *"firefox"*) + print_info "Importing certificates for Firefox..." + echo + ;; + esac - run_firefox + print_info "Loading certificates into $db_root " + echo - print_info "Finished, closing Firefox." + for cert in "$DWNLD_DIR/$CERT_FILENAME/"*."$CERT_EXTENSION" + do + echo "Importing $cert" + certutil -d sql:"$db_root" -A -t TC -n "$cert" -i "$cert" + done - if [ "$backup_exists" == true ] - then - print_info "Migrating user profile into newly installed Firefox" - migrate_ff_profile "migrate" + if ! grep -Pzo 'library=/usr/lib64/libcackey.so\nname=CAC Module\n' "$db_root/$PKCS_FILENAME" >/dev/null + then + printf "library=/usr/lib64/libcackey.so\nname=CAC Module\n" >> "$db_root/$PKCS_FILENAME" + fi fi - repin_firefox - -} # reconfigure_firefox + echo "Done." + echo +} # import_certs -run_firefox () -{ - print_info "Starting Firefox silently to complete post-install actions..." - sudo -H -u "$SUDO_USER" firefox --headless --first-startup >/dev/null 2>&1 & - sleep 3 - pkill -9 firefox - sleep 1 -} -# Discovery of browsers installed on the user's system -# Sets appropriate flags to control the flow of the installation, depending on -# what is needed for the individual user -browser_check () +# Prints message with red [ERROR] tag before the message +print_err () { - print_info "Checking for Firefox and Chrome..." - check_for_firefox - check_for_chrome - - # Browser check results - if [ "$ff_exists" == false ] && [ "$chrome_exists" == false ] - then - print_err "No version of Mozilla Firefox OR Google Chrome has been detected." - print_info "Please install either or both to proceed." - - exit "$E_BROWSER" - - elif [ "$ff_exists" == true ] # Firefox was found - then - if [ "$snap_ff" == true ] # Snap version of Firefox - then - echo -e " - ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}******************** - * The version of Firefox you have installed * - * currently was installed via snap. * - * This version of Firefox is not currently * - * compatible with the method used to enable CAC * - * support in browsers. * - * * - * As a work-around, this script can automatically * - * remove the snap version and reinstall via apt. * - * * - * The option to attempt to migrate all of your * - * personalizations will be given if you choose to * - * replace Firefox via this script. Your Firefox * - * profile will be saved to a temp location, then * - * will overwrite the default profile once the apt * - * version of Firefox has been installed. * - * * - ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}********************\n" + ERR_COLOR='\033[0;31m' # Red for error messages + NO_COLOR='\033[0m' # Revert terminal back to no color - # Prompt user to elect to replace snap firefox with apt firefox - choice='' - while [ "$choice" != "y" ] && [ "$choice" != "n" ] - do - echo -e "\nWould you like to switch to the apt version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" - read -rp '> ' choice - done + echo -e "${ERR_COLOR}[ERROR]${NO_COLOR} $1" +} # print_err - if [ "$choice" == "y" ] - then - reconfigure_firefox - else - if [ $chrome_exists == false ] - then - print_info "You have elected to keep the snap version of Firefox.\n" - print_err "You have no compatible browsers. Exiting..." +# Prints message with yellow [INFO] tag before the message +print_info () +{ + INFO_COLOR='\033[0;33m' # Yellow for notes + NO_COLOR='\033[0m' # Revert terminal back to no color - exit $E_BROWSER - fi - fi - fi - fi - } + echo -e "${INFO_COLOR}[INFO]${NO_COLOR} $1" +} # print_info # Locate and backup the profile for the user's snap version of Firefox # Backup is placed in /tmp/ff_old_profile/ and can be restored after the @@ -345,7 +181,7 @@ check_for_firefox () else # Run Firefox to ensure .mozilla directory has been created echo -e "Running Firefox to generate profile directory..." - sudo -H -u "$SUDO_USER" bash -c 'firefox --headless --first-startup >/dev/null 2>&1 &' + sudo -H -u "$SUDO_USER" bash -c "firefox --headless --first-startup >/dev/null 2>&1 &" sleep 3 pkill -9 firefox sleep 1 @@ -355,18 +191,6 @@ check_for_firefox () print_info "Firefox not found." fi } -# Attempt to find a version of Google Chrome installed on the user's system -check_for_chrome () -{ - # Check to see if Chrome exists - if command -v google-chrome-stable >/dev/null - then - chrome_exists=true - print_info "Found Google Chrome." - else - print_info "Chrome not found." - fi -} # Re-install the user's previous version of Firefox if the snap version was # removed in the process of this script. @@ -387,47 +211,103 @@ revert_firefox () exit "$E_DATABASE" } -# Integrate all certificates into the databases for existing browsers -import_certs () +# Discovery of browsers installed on the user's system +# Sets appropriate flags to control the flow of the installation, depending on +# what is needed for the individual user +browser_check () { - db=$1 - db_root="$(dirname "$db")" - if [ -n "$db_root" ] + print_info "Checking for Firefox and Chrome..." + check_for_firefox + check_for_chrome + + # Browser check results + if [ "$ff_exists" == false ] && [ "$chrome_exists" == false ] then - case "$db_root" in - *"pki"*) - print_info "Importing certificates for Chrome..." - echo - ;; - *"firefox"*) - print_info "Importing certificates for Firefox..." - echo - ;; - esac + print_err "No version of Mozilla Firefox OR Google Chrome has been detected." + print_info "Please install either or both to proceed." - print_info "Loading certificates into $db_root " - echo + exit "$E_BROWSER" - for cert in "$DWNLD_DIR/$CERT_FILENAME/"*."$CERT_EXTENSION" - do - echo "Importing $cert" - certutil -d sql:"$db_root" -A -t TC -n "$cert" -i "$cert" - done + elif [ "$ff_exists" == true ] # Firefox was found + then + if [ "$snap_ff" == true ] # Snap version of Firefox + then + echo -e " + ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}******************** + * The version of Firefox you have installed * + * currently was installed via snap. * + * This version of Firefox is not currently * + * compatible with the method used to enable CAC * + * support in browsers. * + * * + * As a work-around, this script can automatically * + * remove the snap version and reinstall via apt. * + * * + * The option to attempt to migrate all of your * + * personalizations will be given if you choose to * + * replace Firefox via this script. Your Firefox * + * profile will be saved to a temp location, then * + * will overwrite the default profile once the apt * + * version of Firefox has been installed. * + * * + ********************${ERR_COLOR}[ WARNING ]${NO_COLOR}********************\n" + + # Prompt user to elect to replace snap firefox with apt firefox + choice='' + while [ "$choice" != "y" ] && [ "$choice" != "n" ] + do + echo -e "\nWould you like to switch to the apt version of Firefox? ${INFO_COLOR}(y/n)${NO_COLOR}" + read -rp '> ' choice + done - libcackey_path=$(ldconfig -p | awk '/libcackey\.so/ {print $NF}') - if [ -n "$libcackey_path" ]; then - if ! grep -Pzo "library=$libcackey_path\nname=CAC Module\n" "$db_root/$PKCS_FILENAME" >/dev/null + if [ "$choice" == "y" ] then - printf "library=$libcackey_path\nname=CAC Module\n" >> "$db_root/$PKCS_FILENAME" + reconfigure_firefox + else + if [ $chrome_exists == false ] + then + print_info "You have elected to keep the snap version of Firefox.\n" + print_err "You have no compatible browsers. Exiting..." + + exit $E_BROWSER + fi fi - else - print_err "libcackey.so not found. Please ensure the cackey package is installed correctly." fi fi +} # browser_check - echo "Done." - echo -} # import_certs +run_firefox () +{ + print_info "Starting Firefox silently to complete post-install actions..." + sudo -H -u "$SUDO_USER" firefox --headless --first-startup >/dev/null 2>&1 & + sleep 3 + pkill -9 firefox + sleep 1 +} # run_firefox + + +unzip_cert_bundle() +{ + # Unzip cert bundle + if [ -e "$DWNLD_DIR/$BUNDLE_FILENAME" ] + then + mkdir -p "$DWNLD_DIR/$CERT_FILENAME" + unzip "$DWNLD_DIR/$BUNDLE_FILENAME" -d "$DWNLD_DIR/$CERT_FILENAME" + fi +} + +remove_artifacts() +{ + # Remove artifacts + print_info "Removing artifacts..." + rm -rf "${DWNLD_DIR:?}"/{"$BUNDLE_FILENAME","$CERT_FILENAME","$PKG_FILENAME","$FF_PROFILE_NAME"} 2>/dev/null + if [ "$?" -ne "$EXIT_SUCCESS" ] + then + print_err "Failed to remove artifacts" + else + print_info "Done." + fi +} # Check to see if the user has Firefox pinned to their favorites bar in GNOME check_for_ff_pin () @@ -450,6 +330,7 @@ check_for_ff_pin () fi } # check_for_ff_pin +# Repin Firefox to the favorites bar in GNOME repin_firefox () { print_info "Attempting to repin Firefox to favorites bar..." @@ -465,5 +346,3 @@ repin_firefox () print_info "Done." fi } # repin_firefox - -main diff --git a/sh/debian_helper.sh b/sh/debian_helper.sh new file mode 100644 index 0000000..6d7741b --- /dev/null +++ b/sh/debian_helper.sh @@ -0,0 +1,133 @@ +# Replace the current snap version of Firefox with the compatible apt version of Firefox +reconfigure_firefox () +{ + # Replace snap Firefox with version from PPA maintained via Mozilla + + check_for_ff_pin + + #Profile migration + backup_ff_profile + + print_info "Removing Snap version of Firefox" + snap remove --purge firefox + + print_info "Adding PPA for Mozilla maintained Firefox" + add-apt-repository -y ppa:mozillateam/ppa + + print_info "Setting priority to prefer Mozilla PPA over snap package" + echo -e "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox + + print_info "Enabling updates for future Firefox releases" + # shellcheck disable=SC2016 + echo -e 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' > /etc/apt/apt.conf.d/51unattended-upgrades-firefox + + print_info "Installing Firefox via apt" + apt install firefox -y + print_info "Completed re-installation of Firefox" + + # Forget the previous location of firefox executable + if hash firefox + then + hash -d firefox + fi + + run_firefox + + print_info "Finished, closing Firefox." + + if [ "$backup_exists" == true ] + then + print_info "Migrating user profile into newly installed Firefox" + migrate_ff_profile "migrate" + fi + + repin_firefox + +} # reconfigure_firefox + +# Attempt to find a version of Google Chrome installed on the user's system +check_for_chrome() +{ + # Check to see if Chrome exists + if command -v google-chrome >/dev/null + then + chrome_exists=true + print_info "Found Google Chrome." + # Run Chrome to ensure .pki directory has been created +# echo -e "\tRunning Chrome to ensure it has completed post-install actions..." +# # TODO: finish troubleshooting this +# sudo -H -u "$SUDO_USER" bash -c 'google-chrome --headless --disable-gpu >/dev/null 2>&1 &' +# sleep 3 +# pkill -9 google-chrome +# sleep 1 +# echo -e "\tDone." + else + print_info "Chrome not found." + fi +} + + +setup() +{ + mapfile -t databases < <(find "$ORIG_HOME" -name "$DB_FILENAME" 2>/dev/null | grep "firefox\|pki" | grep -v "Trash\|snap") + # Check if databases were found properly + if [ "${#databases[@]}" -eq 0 ] + then + # Database was not found + if [ "$snap_ff" == true ] + then + revert_firefox + else + # Firefox was not replaced, exit with E_DATABASE error + print_err "No valid databases located. Try running, then closing Firefox, then start this script again." + echo -e "\tExiting..." + + exit "$E_DATABASE" + fi + else + # Database was found. (Good) + if [ "$snap_ff" == true ] + then + # Database was found, meaning snap firefox was replaced with apt version + # This conditional branch may not be needed at all... Note: Remove if not needed + snap_ff=false + fi + fi + + # Install middleware and necessary utilities + print_info "Installing middleware..." + apt update + DEBIAN_FRONTEND=noninteractive apt install -y libpcsclite1 pcscd libccid libpcsc-perl pcsc-tools libnss3-tools unzip wget + print_info "Done" + + # Pull all necessary files + print_info "Downloading DoD certificates and Cackey package..." + wget -qP "$DWNLD_DIR" "$CERT_URL" + wget -qP "$DWNLD_DIR" "$CACKEY_URL" + print_info "Done." + + # Install libcackey. + if [ -e "$DWNLD_DIR/$PKG_FILENAME" ] + then + print_info "Installing libcackey..." + if dpkg -i "$DWNLD_DIR/$PKG_FILENAME" + then + print_info "Done." + else + print_err "Installation failed. Exiting..." + + exit "$E_INSTALL" + fi + fi + + # Prevent cackey from upgrading. + # If cackey upgrades beyond 7.5, it moves libcackey.so to a different location, + # breaking Firefox. Returning libcackey.so to the original location does not + # seem to fix this issue. + if apt-mark hold cackey + then + print_info "Hold placed on cackey package" + else + print_err "Failed to place hold on cackey package" + fi +} From 70833312120e4532979e1d85787975e90a2583d1 Mon Sep 17 00:00:00 2001 From: Derrek Landauer Date: Mon, 22 Apr 2024 19:14:56 -0600 Subject: [PATCH 4/6] now passes shellcheck -x --- cac_setup.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cac_setup.sh b/cac_setup.sh index d534942..e7585b1 100755 --- a/cac_setup.sh +++ b/cac_setup.sh @@ -27,6 +27,7 @@ PKG_FILENAME="cackey_0.7.5-1_amd64.deb" CACKEY_URL="http://cackey.rkeene.org/download/0.7.5/$PKG_FILENAME" # source common functions +# shellcheck source=sh/common.sh source "$CUR_DIR"/sh/common.sh # check if distribution is debian-based or arch @@ -36,10 +37,12 @@ check_distro if [[ $distro == "debian" ]]; then # Source the Debian-specific helper script if Debian is detected echo "Debian-based distribution detected." + # shellcheck source=sh/debian_helper.sh source "$CUR_DIR"/sh/debian_helper.sh elif [[ $distro == "arch" ]]; then # Source the Arch-specific helper script if Arch Linux is detected echo "Arch Linux detected." + # shellcheck source=sh/arch_helper.sh source "$CUR_DIR"/sh/arch_helper.sh else echo "The current distribution is not supported by this script." From d8e1c67d3d99ce06d8135240a76c38cabbacd6d3 Mon Sep 17 00:00:00 2001 From: Jeremy Jackson <87815518+jdjaxon@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:05:47 -0400 Subject: [PATCH 5/6] Update static-analysis.yml Added a -x flag to the shellcheck call. --- .github/workflows/static-analysis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 28b40dd..291e4c2 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -20,3 +20,5 @@ jobs: - name: Run ShellCheck uses: ludeeus/action-shellcheck@master + with: + args: -x From fb674ded3152bfac57f20cc44b8ff3741e05f651 Mon Sep 17 00:00:00 2001 From: Jeremy Jackson <87815518+jdjaxon@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:22:55 -0400 Subject: [PATCH 6/6] Update static-analysis.yml Trying a different method to run shellcheck with -x option. --- .github/workflows/static-analysis.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 291e4c2..40ea0a4 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -18,7 +18,8 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - with: - args: -x + - name: Install Shellcheck + run: sudo apt install -y shellcheck + + - name: Run ShellCheck with -x flag + run: shellcheck -x cac_setup.sh