| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619 | #!/bin/bash
#  _____               _           _
# |   __|___ ___ ___ _| |___ _____| |_ ___ ___ ___
# |   __|  _| -_| -_| . | . |     | . | . |   | -_|
# |__|  |_| |___|___|___|___|_|_|_|___|___|_|_|___|
#
#                              Freedom in the Cloud
#
# Alters the security settings
#
# License
# =======
#
# Copyright (C) 2015-2018 Bob Mottram <bob@freedombone.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
PROJECT_NAME='freedombone'
export TEXTDOMAIN=${PROJECT_NAME}-sec
export TEXTDOMAINDIR="/usr/share/locale"
CONFIGURATION_FILE=$HOME/${PROJECT_NAME}.cfg
COMPLETION_FILE=$HOME/${PROJECT_NAME}-completed.txt
UTILS_FILES="/usr/share/${PROJECT_NAME}/utils/${PROJECT_NAME}-utils-*"
for f in $UTILS_FILES
do
    source "$f"
done
SSL_PROTOCOLS=
SSL_CIPHERS=
SSH_CIPHERS=
SSH_MACS=
SSH_KEX=
SSH_HOST_KEY_ALGORITHMS=
SSH_PASSWORDS=
XMPP_CIPHERS=
XMPP_ECC_CURVE=
WEBSITES_DIRECTORY='/etc/nginx/sites-available'
DOVECOT_CIPHERS='/etc/dovecot/conf.d/10-ssl.conf'
SSH_CONFIG='/etc/ssh/sshd_config'
XMPP_CONFIG='/etc/prosody/conf.avail/xmpp.cfg.lua'
MINIMUM_LENGTH=6
IMPORT_FILE=
EXPORT_FILE=
CURRENT_DIR=$(pwd)
DH_KEYLENGTH=2048
LETSENCRYPT_SERVER='https://acme-v01.api.letsencrypt.org/directory'
MY_USERNAME=
function ping_enable_disable {
    ping_str=$"\\nDo you want to enable other systems to ping this machine?\\n\\nPing may be useful for diagnostic purposes, but for added security you may not want to enable it."
    enable_ping="no"
    dialog --title $"Enable Ping / ICMP" \
           --backtitle $"Freedombone Control Panel" \
           --defaultno \
           --yesno "$ping_str" 10 60
    sel=$?
    case $sel in
        0) enable_ping="yes";;
        255) return;;
    esac
    if [[ $enable_ping == "yes" ]]; then
        iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
        iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
        echo "0" >  /proc/sys/net/ipv4/icmp_echo_ignore_all
    else
        iptables -D INPUT -p icmp --icmp-type echo-request -j ACCEPT
        iptables -D OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
        echo "1" >  /proc/sys/net/ipv4/icmp_echo_ignore_all
    fi
}
function any_key_verify {
    echo ''
    read -n1 -rsp $"Press any key to continue or C to check a hash..." key
    if [[ "$key" != 'c' && "$key" != 'C' ]]; then
        return
    fi
    data=$(mktemp 2>/dev/null)
    dialog --title $"Check tripwire hash" \
           --backtitle $"Freedombone Control Panel" \
           --inputbox $"Paste your tripwire hash below and it will be checked against the current database" 12 60 2>"$data"
    sel=$?
    case $sel in
        0)
            GIVEN_HASH=$(<"$data")
            if [ ${#GIVEN_HASH} -gt 8 ]; then
                if [[ "$GIVEN_HASH" == *' '* ]]; then
                    dialog --title $"Check tripwire" \
                           --msgbox $"\\nThe hash should not contain any spaces" 10 40
                else
                    DBHASH=$(sha512sum "/var/lib/tripwire/${HOSTNAME}.twd" | awk -F ' ' '{print $1}')
                    if [[ "$DBHASH" == "$GIVEN_HASH" ]]; then
                        dialog --title $"Check tripwire" \
                               --msgbox $"\\nSuccess\\n\\nThe hash you gave matches the current tripwire database" 10 40
                    else
                        dialog --title $"Check tripwire" \
                               --msgbox $"\\nFailed\\n\\nThe hash you gave does not match the current tripwire database. This might be because you reset the tripwire, or there could have been an unauthorised modification of the system" 12 50
                    fi
                fi
            fi
            ;;
    esac
    rm -f "$data"
}
function show_tripwire_verification_code {
    if [ ! -f "/var/lib/tripwire/${HOSTNAME}.twd" ]; then
        return
    fi
    clear
    echo ''
    echo $'Tripwire Verification Code'
    echo ''
    DBHASH=$(sha512sum "/var/lib/tripwire/${HOSTNAME}.twd")
    echo -n "$DBHASH" | qrencode -t UTF8
    echo ''
    echo "$DBHASH"
    echo ''
}
function reset_tripwire {
    if [ ! -f /usr/bin/reset-tripwire ]; then
        echo $'Missing /usr/bin/reset-tripwire'
        any_key
        return
    fi
    if [ ! -f "/etc/tripwire/${HOSTNAME}-local.key" ]; then
        if [ -f "/etc/tripwire/${PROJECT_NAME}-local.key" ]; then
            # shellcheck disable=SC2086
            mv /etc/tripwire/${PROJECT_NAME}-local.key /etc/tripwire/${HOSTNAME}-local.key
            # shellcheck disable=SC2086
            mv /etc/tripwire/${PROJECT_NAME}-site.key /etc/tripwire/${HOSTNAME}-site.key
        else
            echo $'Error: missing local key'
            any_key
            return
        fi
    fi
    clear
    echo $'Turing off logging...'
    "${PROJECT_NAME}-logging" off
    echo $'Locking down permissions...'
    lockdown_permissions
    echo $'Creating configuration...'
    echo '
       ' | twadmin --create-cfgfile -S "/etc/tripwire/${HOSTNAME}-site.key" /etc/tripwire/twcfg.txt
    echo $'Resetting policy...'
    echo '
       ' | twadmin --create-polfile -S "/etc/tripwire/${HOSTNAME}-site.key" /etc/tripwire/twpol.txt
    echo $'Creating tripwire database'
    echo '
' | tripwire --init --cfgfile /etc/tripwire/tw.cfg --polfile /etc/tripwire/tw.pol --dbfile "/var/lib/tripwire/${HOSTNAME}.twd"
    echo $'Resetting the Tripwire...'
    echo ''
    echo '
                ' | reset-tripwire
    echo ''
    # Sometimes nginx fails to restart if matrix is installed
    # Restart matrix first
    if [ -d /etc/matrix ]; then
        systemctl restart matrix
        systemctl restart nginx
    fi
    if [ -f "/var/lib/tripwire/${HOSTNAME}.twd" ]; then
        show_tripwire_verification_code
        echo $'Tripwire is now reset. Take a note of the above hash, or record'
        echo $'the QR code using a mobile device. This will enable you to independently'
        echo $'verify the integrity of the tripwire.'
    else
        echo $'ERROR: tripwire database was not created'
    fi
    any_key
}
function passwords_show_apps {
    SELECTED_APP=
    i=0
    W=()
    name=()
    # shellcheck disable=SC2068
    for a in ${APPS_AVAILABLE[@]}
    do
        if grep -q "change_password_" "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${a}"; then
            i=$((i+1))
            W+=("$i" "$a")
            name+=("$a")
        fi
    done
    i=$((i+1))
    W+=("$i" "mariadb")
    name+=("mariadb")
    # shellcheck disable=SC2068
    selected_app_index=$(dialog --backtitle $"Freedombone Control Panel" --title $"User $SELECTED_USERNAME: Select App" --menu $"Select one of the following:" 24 40 17 ${W[@]} 3>&2 2>&1 1>&3)
    # shellcheck disable=SC2181
    if [ $? -eq 0 ]; then
        SELECTED_APP="${name[$((selected_app_index-1))]}"
    fi
}
function view_or_change_passwords {
    passwords_select_user
    if [ ! "$SELECTED_USERNAME" ]; then
        return
    fi
    detect_installed_apps
    passwords_show_apps
    if [ ! "$SELECTED_APP" ]; then
        return
    fi
    CURR_PASSWORD=$("${PROJECT_NAME}-pass" -u "${SELECTED_USERNAME}" -a "${SELECTED_APP}")
    icann_address=$(get_app_icann_address "${SELECTED_APP}")
    onion_address=$(get_app_onion_address "${SELECTED_APP}")
    titlestr=$"View or Change Password"
    if [ ${#onion_address} -gt 0 ]; then
        viewstr=$"${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address or $onion_address\\n\\nCopy or change it if you wish."
    else
        viewstr=$"${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address\\n\\nCopy or change it if you wish."
    fi
    if [ -f /root/.nostore ]; then
        titlestr=$"Change Password"
        if [ ${#onion_address} -gt 0 ]; then
            viewstr=$"Change the ${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address or $onion_address."
        else
            viewstr=$"Change the ${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address."
        fi
    fi
    if [[ "${SELECTED_APP}" == 'mariadb' ]]; then
        CURR_PASSWORD=$("${PROJECT_NAME}-pass" -u root -a mariadb)
        dialog --title $"MariaDB database password" \
               --msgbox "\\n            ${CURR_PASSWORD}" 7 40
        return
    fi
    data=$(mktemp 2>/dev/null)
    dialog --title "$titlestr" \
           --backtitle $"Freedombone Control Panel" \
           --inputbox "$viewstr" 12 75 "$CURR_PASSWORD" 2>"$data"
    sel=$?
    case $sel in
        0)
            CURR_PASSWORD=$(<"$data")
            if [ ${#CURR_PASSWORD} -gt 8 ]; then
                "${PROJECT_NAME}-pass" -u "${SELECTED_USERNAME}" -a "${SELECTED_APP}" -p "${CURR_PASSWORD}"
                "change_password_${SELECTED_APP}" "${SELECTED_USERNAME}" "${CURR_PASSWORD}"
                dialog --title $"Change password" \
                       --msgbox $"The password was changed" 6 40
            else
                dialog --title $"Change password" \
                       --msgbox $"The password given must be at least 8 characters" 6 40
            fi
            ;;
    esac
    rm -f "$data"
}
function show_firewall {
    W=()
    while read -r line; do
        firewall_name=$(echo "$line" | awk -F '=' '{print $1}')
        firewall_port=$(echo "$line" | awk -F '=' '{print $2}')
        W+=("${firewall_name}" "${firewall_port}")
    done < "$FIREWALL_CONFIG"
    # shellcheck disable=SC2068
    dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Firewall" --menu $"Press ESC to return to main menu" 28 50 28 "${W[@]}" 3>&2 2>&1 1>&3
}
function export_passwords {
    detect_usb_drive
    dialog --title $"Export passwords to USB drive $USB_DRIVE" \
           --backtitle $"Security Settings" \
           --defaultno \
           --yesno $"\\nPlease confirm that you wish to export passwords to a LUKS formatted USB drive. The drive should be plugged in." 10 60
    sel=$?
    case $sel in
        1) return;;
        255) return;;
    esac
    dialog --title $"Export passwords to USB drive $USB_DRIVE" \
           --backtitle $"Security Settings" \
           --defaultno \
           --yesno $"\\nDo you need to format the drive as LUKS encrypted?" 12 60
    sel=$?
    case $sel in
        0) ${PROJECT_NAME}-format "$USB_DRIVE";;
    esac
    clear
    backup_mount_drive "${USB_DRIVE}"
    ${PROJECT_NAME}-pass --export "${USB_MOUNT}/${PROJECT_NAME}-passwords.xml"
    backup_unmount_drive "${USB_DRIVE}"
}
function get_protocols_from_website {
    if [ ! -f "$WEBSITES_DIRECTORY/$1" ]; then
        return
    fi
    SSL_PROTOCOLS=$(grep 'ssl_protocols ' "$WEBSITES_DIRECTORY/$1" | awk -F "ssl_protocols " '{print $2}' | awk -F ';' '{print $1}')
}
function get_ciphers_from_website {
    if [ ! -f "$WEBSITES_DIRECTORY/$1" ]; then
        return
    fi
    SSL_CIPHERS=$(grep 'ssl_ciphers ' "$WEBSITES_DIRECTORY/$1" | awk -F "ssl_ciphers " '{print $2}' | awk -F "'" '{print $2}')
}
function get_imap_settings {
    if [ ! -f $DOVECOT_CIPHERS ]; then
        return
    fi
    # clear commented out cipher list
    sed -i "s|#ssl_cipher_list.*||g" $DOVECOT_CIPHERS
    if [ "$SSL_CIPHERS" ]; then
        return
    fi
    if [ ${#SSL_CIPHERS} -gt $MINIMUM_LENGTH ]; then
        return
    fi
    SSL_CIPHERS=$(grep 'ssl_cipher_list' "$DOVECOT_CIPHERS" | awk -F '=' '{print $2}' | awk -F "'" '{print $2}')
}
function get_xmpp_settings {
    if [ ! -f $XMPP_CONFIG ]; then
        return
    fi
    XMPP_CIPHERS=$(grep 'ciphers ' "$XMPP_CONFIG" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}')
    XMPP_ECC_CURVE=$(grep 'curve ' "$XMPP_CONFIG" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}')
}
function get_ssh_settings {
    if [ -f $SSH_CONFIG ]; then
        SSH_PASSWORDS=$(grep 'PasswordAuthentication ' "$SSH_CONFIG" | awk -F 'PasswordAuthentication ' '{print $2}')
    fi
    if [ -f /etc/ssh/ssh_config ]; then
        SSH_HOST_KEY_ALGORITHMS=$(grep 'HostKeyAlgorithms ' "/etc/ssh/ssh_config" | awk -F 'HostKeyAlgorithms ' '{print $2}')
    fi
}
function change_website_settings {
    if [ ! "$SSL_PROTOCOLS" ]; then
        return
    fi
    if [ ! "$SSL_CIPHERS" ]; then
        return
    fi
    if [ ${#SSL_PROTOCOLS} -lt $MINIMUM_LENGTH ]; then
        return
    fi
    if [ ${#SSL_CIPHERS} -lt $MINIMUM_LENGTH ]; then
        return
    fi
    if [ ! -d $WEBSITES_DIRECTORY ]; then
        return
    fi
    cd $WEBSITES_DIRECTORY || exit 2468724628
    for file in $(dir -d "*") ; do
        sed -i "s|ssl_protocols .*|ssl_protocols $SSL_PROTOCOLS;|g" "$WEBSITES_DIRECTORY/$file"
        if ! grep -q "Mobile compatible ciphers" "$WEBSITES_DIRECTORY/$file"; then
            sed -i "s|ssl_ciphers .*|ssl_ciphers '$SSL_CIPHERS';|g" "$WEBSITES_DIRECTORY/$file"
        else
            sed -i "s|ssl_ciphers .*|ssl_ciphers '$SSL_CIPHERS_MOBILE';|g" "$WEBSITES_DIRECTORY/$file"
        fi
    done
    systemctl restart nginx
    echo $'Web security settings changed'
}
function change_imap_settings {
    if [ ! -f $DOVECOT_CIPHERS ]; then
        return
    fi
    if [ ! "$SSL_CIPHERS" ]; then
        return
    fi
    if [ ${#SSL_CIPHERS} -lt $MINIMUM_LENGTH ]; then
        return
    fi
    sed -i "s|ssl_cipher_list.*|ssl_cipher_list = '$SSL_CIPHERS'|g" $DOVECOT_CIPHERS
    sed -i "s|ssl_protocols.*|ssl_protocols = '$SSL_PROTOCOLS'|g" $DOVECOT_CIPHERS
    systemctl restart dovecot
    echo $'imap security settings changed'
}
function change_ssh_settings {
    if [ -f /etc/ssh/ssh_config ]; then
        if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
            sed -i "s|HostKeyAlgorithms .*|HostKeyAlgorithms $SSH_HOST_KEY_ALGORITHMS|g" /etc/ssh/ssh_config
            echo $'ssh client security settings changed'
        fi
    fi
    if [ -f $SSH_CONFIG ]; then
        if [ ! $SSH_CIPHERS ]; then
            return
        fi
        if [ ! $SSH_MACS ]; then
            return
        fi
        if [ ! $SSH_KEX ]; then
            return
        fi
        if [ ! "$SSH_PASSWORDS" ]; then
            SSH_PASSWORDS='yes'
        fi
        sed -i "s|Ciphers .*|Ciphers $SSH_CIPHERS|g" $SSH_CONFIG
        sed -i "s|MACs .*|MACs $SSH_MACS|g" $SSH_CONFIG
        sed -i "s|KexAlgorithms .*|KexAlgorithms $SSH_KEX|g" $SSH_CONFIG
        sed -i "s|#PasswordAuthentication .*|PasswordAuthentication $SSH_PASSWORDS|g" $SSH_CONFIG
        sed -i "s|PasswordAuthentication .*|PasswordAuthentication $SSH_PASSWORDS|g" $SSH_CONFIG
        systemctl restart ssh
        echo $'ssh server security settings changed'
    fi
}
function change_xmpp_settings {
    if [ ! -f $XMPP_CONFIG ]; then
        return
    fi
    if [ ! "$XMPP_CIPHERS" ]; then
        return
    fi
    if [ ! "$XMPP_ECC_CURVE" ]; then
        return
    fi
    sed -i "s|ciphers =.*|ciphers = \"$XMPP_CIPHERS\";|g" $XMPP_CONFIG
    sed -i "s|curve =.*|curve = \"$XMPP_ECC_CURVE\";|g" $XMPP_CONFIG
    systemctl restart prosody
    echo $'xmpp security settings changed'
}
function allow_ssh_passwords {
    dialog --title $"SSH Passwords" \
           --backtitle $"Freedombone Security Configuration" \
           --yesno $"\\nAllow SSH login using passwords?" 7 60
    sel=$?
    case $sel in
        0) SSH_PASSWORDS="yes";;
        1) SSH_PASSWORDS="no";;
        255) exit 0;;
    esac
}
function interactive_setup {
    if [ "$SSL_CIPHERS" ]; then
        data=$(mktemp 2>/dev/null)
        dialog --backtitle $"Freedombone Security Configuration" \
               --form $"\\nWeb/IMAP Ciphers:" 10 95 2 \
               $"Protocols:" 1 1 "$SSL_PROTOCOLS" 1 15 90 90 \
               $"Ciphers:" 2 1 "$SSL_CIPHERS" 2 15 90 512 \
               2> "$data"
        sel=$?
        case $sel in
            1) SSL_PROTOCOLS=$(sed -n 1p < "$data")
               SSL_CIPHERS=$(sed -n 2p < "$data")
               ;;
            255) rm -f "$data"
                 exit 0;;
        esac
        rm -f "$data"
    fi
    data=$(mktemp 2>/dev/null)
    if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
        dialog --backtitle $"Freedombone Security Configuration" \
               --form $"\\nSecure Shell Ciphers:" 13 95 4 \
               $"Ciphers:" 1 1 "$SSH_CIPHERS" 1 15 90 512 \
               $"MACs:" 2 1 "$SSH_MACS" 2 15 90 512 \
               $"KEX:" 3 1 "$SSH_KEX" 3 15 90 512 \
               $"Host key algorithms:" 4 1 "$SSH_HOST_KEY_ALGORITHMS" 4 15 90 512 \
               2> "$data"
        sel=$?
        case $sel in
            1) SSH_CIPHERS=$(sed -n 1p < "$data")
               SSH_MACS=$(sed -n 2p < "$data")
               SSH_KEX=$(sed -n 3p < "$data")
               SSH_HOST_KEY_ALGORITHMS=$(sed -n 4p < "$data")
               ;;
            255) rm -f "$data"
                 exit 0;;
        esac
    else
        dialog --backtitle $"Freedombone Security Configuration" \
               --form $"\\nSecure Shell Ciphers:" 11 95 3 \
               $"Ciphers:" 1 1 "$SSH_CIPHERS" 1 15 90 512 \
               $"MACs:" 2 1 "$SSH_MACS" 2 15 90 512 \
               $"KEX:" 3 1 "$SSH_KEX" 3 15 90 512 \
               2> "$data"
        sel=$?
        case $sel in
            1) SSH_CIPHERS=$(sed -n 1p < "$data")
               SSH_MACS=$(sed -n 2p < "$data")
               SSH_KEX=$(sed -n 3p < "$data")
               ;;
            255) rm -f "$data"
                 exit 0;;
        esac
    fi
    rm -f "$data"
    if [ "$XMPP_CIPHERS" ]; then
        data=$(mktemp 2>/dev/null)
        dialog --backtitle $"Freedombone Security Configuration" \
               --form $"\\nXMPP Ciphers:" 10 95 2 \
               $"Ciphers:" 1 1 "$XMPP_CIPHERS" 1 15 90 512 \
               $"ECC Curve:" 2 1 "$XMPP_ECC_CURVE" 2 15 50 50 \
               2> "$data"
        sel=$?
        case $sel in
            1) XMPP_CIPHERS=$(sed -n 1p < "$data")
               XMPP_ECC_CURVE=$(sed -n 2p < "$data")
               ;;
            255) rm -f "$data"
                 exit 0;;
        esac
        rm -f "$data"
    fi
    dialog --title $"Final Confirmation" \
           --backtitle $"Freedombone Security Configuration" \
           --defaultno \
           --yesno $"\\nPlease confirm that you wish your security settings to be changed?\\n\\nWARNING: any mistakes made in the security settings could compromise your system, so be extra careful when answering 'yes'." 12 60
    sel=$?
    case $sel in
        1) clear
           echo $'Exiting without changing security settings'
           exit 0;;
        255) clear
             echo $'Exiting without changing security settings'
             exit 0;;
    esac
    clear
}
function send_monkeysphere_server_keys_to_users {
    monkeysphere_server_keys=$(monkeysphere-host show-key | grep $"OpenPGP fingerprint" | awk -F ' ' '{print $3}')
    for d in /home/*/ ; do
        USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
        if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
            if [ ! -d "/home/$USERNAME/.monkeysphere" ]; then
                mkdir "/home/$USERNAME/.monkeysphere"
            fi
            echo "$monkeysphere_server_keys" > "/home/$USERNAME/.monkeysphere/server_keys"
            chown -R "$USERNAME":"$USERNAME" "/home/$USERNAME/.monkeysphere"
        fi
    done
}
function regenerate_ssh_host_keys {
    rm -f /etc/ssh/ssh_host_*
    dpkg-reconfigure openssh-server
    echo $'ssh host keys regenerated'
    # remove small moduli
    awk '$5 > 2000' /etc/ssh/moduli > ~/moduli
    mv ~/moduli /etc/ssh/moduli
    echo $'ssh small moduli removed'
    # update monkeysphere
    DEFAULT_DOMAIN_NAME=
    read_config_param "DEFAULT_DOMAIN_NAME"
    monkeysphere-host import-key /etc/ssh/ssh_host_rsa_key "ssh://$DEFAULT_DOMAIN_NAME"
    SSH_ONION_HOSTNAME=$(grep 'ssh onion domain' "${COMPLETION_FILE}" | awk -F ':' '{print $2}')
    monkeysphere-host import-key /etc/ssh/ssh_host_rsa_key "ssh://$SSH_ONION_HOSTNAME"
    monkeysphere-host publish-key
    send_monkeysphere_server_keys_to_users
    echo $'updated monkeysphere ssh host key'
    systemctl restart ssh
}
function regenerate_dh_keys {
    if [ ! -d /etc/ssl/mycerts ]; then
        echo $'No dhparam certificates were found'
        return
    fi
    data=$(mktemp 2>/dev/null)
    dialog --backtitle "Freedombone Security Configuration" \
           --title "Diffie-Hellman key length" \
           --radiolist "The smaller length is better suited to low power embedded systems:" 12 40 3 \
           1 "2048 bits" off \
           2 "3072 bits" on \
           3 "4096 bits" off 2> "$data"
    sel=$?
    case $sel in
        1) rm -f "$data"
           exit 1;;
        255) rm -f "$data"
             exit 1;;
    esac
    case $(cat "$data") in
        1) DH_KEYLENGTH=2048;;
        2) DH_KEYLENGTH=3072;;
        3) DH_KEYLENGTH=4096;;
    esac
    rm -f "$data"
    ${PROJECT_NAME}-dhparam --recalc yes -l ${DH_KEYLENGTH}
}
function renew_startssl {
    renew_domain=
    data=$(mktemp 2>/dev/null)
    dialog --title $"Renew a StartSSL certificate" \
           --backtitle $"Freedombone Security Settings" \
           --inputbox $"Enter the domain name" 8 60 2>"$data"
    sel=$?
    case $sel in
        0)
            renew_domain=$(<"$data")
            ;;
    esac
    rm -f "$data"
    if [ ! "$renew_domain" ]; then
        return
    fi
    if [[ $renew_domain == "http"* ]]; then
        dialog --title $"Renew a StartSSL certificate" \
               --msgbox $"Don't include the https://" 6 40
        return
    fi
    if [ ! -f "/etc/ssl/certs/${renew_domain}.dhparam" ]; then
        dialog --title $"Renew a StartSSL certificate" \
               --msgbox $"An existing certificate for $renew_domain was not found" 6 40
        return
    fi
    if [[ $renew_domain != *"."* ]]; then
        dialog --title $"Renew a StartSSL certificate" \
               --msgbox $"Invalid domain name: $renew_domain" 6 40
        return
    fi
    ${PROJECT_NAME}-renew-cert -h "$renew_domain" -p startssl
    exit 0
}
function renew_letsencrypt {
    renew_domain=
    data=$(mktemp 2>/dev/null)
    dialog --title $"Renew a Let's Encrypt certificate" \
           --backtitle $"Freedombone Security Settings" \
           --inputbox $"Enter the domain name" 8 60 2>"$data"
    sel=$?
    case $sel in
        0)
            renew_domain=$(<"$data")
            ;;
    esac
    rm -f "$data"
    if [ ! "$renew_domain" ]; then
        return
    fi
    if [[ $renew_domain == "http"* ]]; then
        dialog --title $"Renew a Let's Encrypt certificate" \
               --msgbox $"Don't include the https://" 6 40
        return
    fi
    if [ ! -f "/etc/ssl/certs/${renew_domain}.dhparam" ]; then
        dialog --title $"Renew a Let's Encrypt certificate" \
               --msgbox $"An existing certificate for $renew_domain was not found" 6 40
        return
    fi
    if [[ $renew_domain != *"."* ]]; then
        dialog --title $"Renew a Let's Encrypt certificate" \
               --msgbox $"Invalid domain name: $renew_domain" 6 40
        return
    fi
    ${PROJECT_NAME}-renew-cert -h "$renew_domain" -p 'letsencrypt'
    exit 0
}
function delete_letsencrypt {
    delete_domain=
    data=$(mktemp 2>/dev/null)
    dialog --title $"Delete a Let's Encrypt certificate" \
           --backtitle $"Freedombone Security Settings" \
           --inputbox $"Enter the domain name" 8 60 2>"$data"
    sel=$?
    case $sel in
        0)
            delete_domain=$(<"$data")
            ;;
    esac
    rm -f "$data"
    if [ ! "$delete_domain" ]; then
        return
    fi
    if [[ $delete_domain == "http"* ]]; then
        dialog --title $"Delete a Let's Encrypt certificate" \
               --msgbox $"Don't include the https://" 6 40
        return
    fi
    if [ ! -f "/etc/ssl/certs/${delete_domain}.dhparam" ]; then
        dialog --title $"Delete a Let's Encrypt certificate" \
               --msgbox $"An existing certificate for $renew_domain was not found" 6 40
        return
    fi
    if [[ $delete_domain != *"."* ]]; then
        dialog --title $"Delete a Let's Encrypt certificate" \
               --msgbox $"Invalid domain name: $delete_domain" 6 40
        return
    fi
    dialog --title $"Delete a Let's Encrypt certificate" \
           --backtitle $"Freedombone Security Settings" \
           --defaultno \
           --yesno $"\\nConfirm deletion of the TLS certificate for $delete_domain ?" 7 60
    sel=$?
    case $sel in
        0) ${PROJECT_NAME}-addcert -r "$delete_domain";;
        255) exit 0;;
    esac
    exit 0
}
function create_letsencrypt {
    new_domain=
    data=$(mktemp 2>/dev/null)
    dialog --title $"Create a new Let's Encrypt certificate" \
           --backtitle $"Freedombone Security Settings" \
           --inputbox $"Enter the domain name" 8 60 2>"$data"
    sel=$?
    case $sel in
        0)
            new_domain=$(<"$data")
            ;;
    esac
    rm -f "$data"
    if [ ! "$new_domain" ]; then
        return
    fi
    if [[ $new_domain == "http"* ]]; then
        dialog --title $"Create a new Let's Encrypt certificate" \
               --msgbox $"Don't include the https://" 6 40
        return
    fi
    if [[ $new_domain != *"."* ]]; then
        dialog --title $"Create a new Let's Encrypt certificate" \
               --msgbox $"Invalid domain name: $new_domain" 6 40
        return
    fi
    if [ ! -d "/var/www/${new_domain}" ]; then
        domain_found=
        if [ -f /etc/nginx/sites-available/radicale ]; then
            if grep -q "${new_domain}" /etc/nginx/sites-available/radicale; then
                domain_found=1
            fi
        fi
        if [ -f "/etc/nginx/sites-available/${new_domain}" ]; then
            domain_found=1
        fi
        if [[ "${new_domain}" == "jitsi"* || "${new_domain}" == "meet"* ]]; then
            domain_found=1
        fi
        if [ ! $domain_found ]; then
            dialog --title $"Create a new Let's Encrypt certificate" \
                   --msgbox $'Domain not found within /var/www' 6 40
            return
        fi
    fi
    ${PROJECT_NAME}-addcert -e "$new_domain" -s "$LETSENCRYPT_SERVER" --dhkey "$DH_KEYLENGTH"
    exit 0
}
function update_ciphersuite {
    RECOMMENDED_SSL_CIPHERS="$SSL_CIPHERS"
    if [ ${#RECOMMENDED_SSL_CIPHERS} -lt 5 ]; then
        return
    fi
    RECOMMENDED_SSL_PROTOCOLS="$SSL_PROTOCOLS"
    if [ ${#RECOMMENDED_SSL_PROTOCOLS} -lt 5 ]; then
        return
    fi
    RECOMMENDED_SSH_CIPHERS="$SSH_CIPHERS"
    if [ ${#RECOMMENDED_SSH_CIPHERS} -lt 5 ]; then
        return
    fi
    RECOMMENDED_SSH_MACS="$SSH_MACS"
    if [ ${#RECOMMENDED_SSH_MACS} -lt 5 ]; then
        return
    fi
    RECOMMENDED_SSH_KEX="$SSH_KEX"
    if [ ${#RECOMMENDED_SSH_KEX} -lt 5 ]; then
        return
    fi
    cd $WEBSITES_DIRECTORY || exit 728425476
    for file in $(dir -d "*") ; do
        sed -i "s|ssl_protocols .*|ssl_protocols $RECOMMENDED_SSL_PROTOCOLS;|g" "$WEBSITES_DIRECTORY/$file"
        if ! grep -q "Mobile compatible ciphers" "$WEBSITES_DIRECTORY/$file"; then
            sed -i "s|ssl_ciphers .*|ssl_ciphers '$RECOMMENDED_SSL_CIPHERS';|g" "$WEBSITES_DIRECTORY/$file"
        else
            sed -i "s|ssl_ciphers .*|ssl_ciphers '$SSL_CIPHERS_MOBILE';|g" "$WEBSITES_DIRECTORY/$file"
        fi
    done
    systemctl restart nginx
    write_config_param "SSL_PROTOCOLS" "$RECOMMENDED_SSL_PROTOCOLS"
    write_config_param "SSL_CIPHERS" "$RECOMMENDED_SSL_CIPHERS"
    sed -i "s|Ciphers .*|Ciphers $RECOMMENDED_SSH_CIPHERS|g" $SSH_CONFIG
    sed -i "s|MACs .*|MACs $RECOMMENDED_SSH_MACS|g" $SSH_CONFIG
    sed -i "s|KexAlgorithms .*|KexAlgorithms $RECOMMENDED_SSH_KEX|g" $SSH_CONFIG
    systemctl restart ssh
    write_config_param "SSH_CIPHERS" "$RECOMMENDED_SSH_CIPHERS"
    write_config_param "SSH_MACS" "$RECOMMENDED_SSH_MACS"
    write_config_param "SSH_KEX" "$RECOMMENDED_SSH_KEX"
    dialog --title $"Update ciphersuite" \
           --msgbox $"The ciphersuite has been updated to recommended versions" 6 40
    exit 0
}
function enable_monkeysphere {
    monkey=
    dialog --title $"GPG based authentication" \
           --backtitle $"Freedombone Security Configuration" \
           --defaultno \
           --yesno $"\\nEnable GPG based authentication with monkeysphere ?" 7 60
    sel=$?
    case $sel in
        0) monkey='yes';;
        255) exit 0;;
    esac
    if [ $monkey ]; then
        read_config_param "MY_USERNAME"
        if [ ! -f /home/$MY_USERNAME/.monkeysphere/authorized_user_ids ]; then
            dialog --title $"GPG based authentication" \
                   --msgbox $"$MY_USERNAME does not currently have any ids within ~/.monkeysphere/authorized_user_ids" 6 40
            exit 0
        fi
        MY_GPG_PUBLIC_KEY_ID=$(gpg_pubkey_from_email "$MY_USERNAME" "$MY_USERNAME@$HOSTNAME")
        if [ ${#MY_GPG_PUBLIC_KEY_ID} -lt 4 ]; then
            echo $"monkeysphere unable to get GPG key ID for user $MY_USERNAME@$HOSTNAME"
            exit 52825
        fi
        sed -i 's|#AuthorizedKeysFile|AuthorizedKeysFile|g' /etc/ssh/sshd_config
        sed -i 's|AuthorizedKeysFile.*|AuthorizedKeysFile /var/lib/monkeysphere/authorized_keys/%u|g' /etc/ssh/sshd_config
        monkeysphere-authentication update-users
        # The admin user is the identity certifier
        fpr=$(gpg --with-colons --fingerprint "$MY_GPG_PUBLIC_KEY_ID" | grep fpr | head -n 1 | awk -F ':' '{print $10}')
        monkeysphere-authentication add-identity-certifier "$fpr"
        monkeysphere-host publish-key
        send_monkeysphere_server_keys_to_users
    else
        sed -i 's|#AuthorizedKeysFile|AuthorizedKeysFile|g' /etc/ssh/sshd_config
        sed -i 's|AuthorizedKeysFile.*|AuthorizedKeysFile %h/.ssh/authorized_keys|g' /etc/ssh/sshd_config
    fi
    systemctl restart ssh
    if [ $monkey ]; then
        dialog --title $"GPG based authentication" \
               --msgbox $"GPG based authentication was enabled" 6 40
    else
        dialog --title $"GPG based authentication" \
               --msgbox $"GPG based authentication was disabled" 6 40
    fi
    exit 0
}
function register_website {
    domain="$1"
    if [[ ${domain} == *".local" ]]; then
        echo $"Can't register local domains"
        return
    fi
    if [ ! -f "/etc/ssl/private/${domain}.key" ]; then
        echo $"No SSL/TLS private key found for ${domain}"
        return
    fi
    if [ ! -f "/etc/nginx/sites-available/${domain}" ]; then
        echo $"No virtual host found for ${domain}"
        return
    fi
    monkeysphere-host import-key "/etc/ssl/private/${domain}.key" "https://${domain}"
    monkeysphere-host publish-key
    echo "0"
}
function register_website_interactive {
    data=$(mktemp 2>/dev/null)
    dialog --title $"Register a website with monkeysphere" \
           --backtitle $"Freedombone Security Settings" \
           --inputbox $"Enter the website domain name (without https://)" 8 60 2>"$data"
    sel=$?
    case $sel in
        0)
            domain=$(<"$data")
            if ! register_website "$domain"; then
                dialog --title $"Register a website with monkeysphere" \
                       --msgbox "$?" 6 40
            else
                dialog --title $"Register a website with monkeysphere" \
                       --msgbox $"$domain has been registered" 6 40
            fi
            ;;
    esac
    rm -f "$data"
}
function pin_all_tls_certs {
    ${PROJECT_NAME}-pin-cert all
}
function remove_pinning {
    data=$(mktemp 2>/dev/null)
    dialog --title $"Remove pinning for a domain" \
           --backtitle $"Freedombone Security Settings" \
           --inputbox $"Enter the website domain name (without https://)" 8 60 2>"$data"
    sel=$?
    case $sel in
        0)
            domain=$(<"$data")
            if ! ${PROJECT_NAME}-pin-cert "$domain" remove; then
                dialog --title $"Removed pinning from $domain" \
                       --msgbox "$?" 6 40
            fi
            ;;
    esac
    rm -f "$data"
}
function store_passwords {
    dialog --title $"Store Passwords" \
           --backtitle $"Freedombone Security Configuration" \
           --yesno $"\\nDo you wish to store passwords on the system? Stored passwords are convenient but carry some additional security risk." 10 60
    sel=$?
    case $sel in
        0)
            if [ -f /root/.nostore ]; then
                read_config_param "MY_USERNAME"
                if [ ! -f /home/$MY_USERNAME/.ssh/authorized_keys ]; then
                    dialog --title $"Store Passwords" \
                           --msgbox $"\\nYou should first enable key based ssh login to improve security" 8 60
                    return
                fi
                if [[ $SSH_PASSWORDS == 'yes' ]]; then
                    dialog --title $"Store Passwords" \
                           --msgbox $"\\nYou should disable ssh passwords to improve security" 8 60
                    return
                fi
                ${PROJECT_NAME}-pass --enable yes
                dialog --title $"Store Passwords" \
                       --msgbox $"\\nUser passwords will now be stored on the system" 8 60
            fi
            return
            ;;
        1)
            ${PROJECT_NAME}-pass --clear yes
            dialog --title $"Passwords were removed and will not be stored" \
                   --msgbox $"\\nFor the best security you should now manually change passwords via web interfaces so that there is no possibility of them being recovered from the disk" 9 60
            return
            ;;
        255) return;;
    esac
}
function show_tor_bridges {
    clear
    bridges_list=$(grep "Bridge " /etc/tor/torrc | grep -v '##')
    if [ ${#bridges_list} -eq 0 ]; then
        echo $'No Tor bridges have been added'
        return
    fi
    echo "${bridges_list}"
}
function add_tor_bridge {
    data=$(mktemp 2>/dev/null)
    dialog --backtitle $"Freedombone Control Panel" \
           --title $"Add obfs4 Tor bridge" \
           --form "\\n" 9 60 4 \
           $"IP address:   " 1 1 "   .   .   .   " 1 17 16 16 \
           $"Port:         " 2 1 "" 2 17 8 8 \
           $"Key/Nickname: " 3 1 "" 3 17 250 250 \
           2> "$data"
    sel=$?
    case $sel in
        1) rm -f "$data"
           return;;
        255) rm -f "$data"
             return;;
    esac
    bridge_ip_address=$(sed -n 1p < "$data")
    bridge_port=$(sed -n 2p < "$data")
    bridge_key=$(sed -n 3p < "$data")
    rm -f "$data"
    if [[ "${bridge_ip_address}" == *" "* ]]; then
        return
    fi
    if [[ "${bridge_ip_address}" != *"."* ]]; then
        return
    fi
    if [ ${#bridge_port} -eq 0 ]; then
        return
    fi
    if [ ${#bridge_key} -eq 0 ]; then
        return
    fi
    tor_add_bridge "${bridge_ip_address}" "${bridge_port}" "${bridge_key}"
    dialog --title $"Add obfs4 Tor bridge" \
           --msgbox $"Bridge added" 6 40
}
function remove_tor_bridge {
    bridge_removed=
    data=$(mktemp 2>/dev/null)
    dialog --title $"Remove obfs4 Tor bridge" \
           --backtitle $"Freedombone Control Panel" \
           --inputbox $"Enter the IP address, key or Nickname of the bridge" 8 65 2>"$data"
    sel=$?
    case $sel in
        0)
            response=$(<"$data")
            if [ ${#response} -gt 2 ]; then
                if [[ "${response}" != *" "* ]]; then
                    if [[ "${response}" == *"."* ]]; then
                        if grep -q "Bridge ${response}" /etc/tor/torrc; then
                            tor_remove_bridge "${response}"
                            bridge_removed=1
                        fi
                    else
                        if grep -q " $response" /etc/tor/torrc; then
                            tor_remove_bridge "${response}"
                            bridge_removed=1
                        fi
                    fi
                fi
            fi
            ;;
    esac
    rm -f "$data"
    if [ $bridge_removed ]; then
        dialog --title $"Remove obfs4 Tor bridge" \
               --msgbox $"Bridge removed" 6 40
    fi
}
function add_tor_bridge_relay {
    read_config_param 'TOR_BRIDGE_NICKNAME'
    read_config_param 'TOR_BRIDGE_PORT'
    # remove any previous bridge port from the firewall
    if [ ${#TOR_BRIDGE_PORT} -gt 0 ]; then
        firewall_remove "$TOR_BRIDGE_PORT" tcp
    fi
    data=$(mktemp 2>/dev/null)
    dialog --backtitle $"Freedombone Control Panel" \
           --title $"Become an obfs4 Tor bridge relay" \
           --form "\\n" 8 60 2 \
           $"Bridge Nickname: " 1 1 "$TOR_BRIDGE_NICKNAME" 1 20 250 250 \
           2> "$data"
    sel=$?
    case $sel in
        1) rm -f "$data"
           return;;
        255) rm -f "$data"
             return;;
    esac
    bridge_nickname=$(sed -n 1p < "$data")
    rm -f "$data"
    if [[ "${bridge_nickname}" == *" "* ]]; then
        return
    fi
    if [ ${#bridge_nickname} -eq 0 ]; then
        return
    fi
    TOR_BRIDGE_NICKNAME="$bridge_nickname"
    TOR_BRIDGE_PORT=$((20000 + RANDOM % 40000))
    write_config_param 'TOR_BRIDGE_NICKNAME' "$TOR_BRIDGE_NICKNAME"
    write_config_param 'TOR_BRIDGE_PORT' "$TOR_BRIDGE_PORT"
    tor_create_bridge_relay
    dialog --title $"You are now an obfs4 Tor bridge relay" \
           --msgbox $"\\nIP address: $(get_ipv4_address)\\n\\nPort: ${TOR_BRIDGE_PORT}\\n\\nNickname: ${TOR_BRIDGE_NICKNAME}" 10 65
}
function remove_tor_bridge_relay {
    tor_remove_bridge_relay
    dialog --title $"Remove Tor bridge relay" \
           --msgbox $"Bridge relay removed" 10 60
}
function menu_tor_bridges {
    W=(1 $"Show bridges"
       2 $"Add a bridge"
       3 $"Remove a bridge"
       4 $"Make this system into a bridge"
       5 $"Stop being a bridge")
    # shellcheck disable=SC2068
    selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Security Settings" --menu $"Choose an operation, or ESC to go back:" 14 50 6 "${W[@]}" 3>&2 2>&1 1>&3)
    if [ ! "$selection" ]; then
        exit 0
    fi
    case $selection in
        1)
            show_tor_bridges
            exit 0
            ;;
        2)
            add_tor_bridge
            exit 0
            ;;
        3)
            remove_tor_bridge
            exit 0
            ;;
        4)
            add_tor_bridge_relay
            exit 0
            ;;
        5)
            remove_tor_bridge_relay
            exit 0
            ;;
    esac
}
function menu_security_settings {
    W=(1 $"Passwords"
       2 $"Run STIG tests"
       3 $"Fix STIG test failures"
       4 $"Show tripwire verification code"
       5 $"Reset tripwire"
       6 $"Enable or disable ping"
       7 $"Show ssh host public key"
       8 $"Tor bridges"
       9 $"Password storage"
       10 $"Export passwords"
       11 $"Regenerate ssh host keys"
       12 $"Regenerate Diffie-Hellman keys"
       13 $"Update cipersuite"
       14 $"Create a new Let's Encrypt certificate"
       15 $"Renew Let's Encrypt certificate"
       16 $"Delete a Let's Encrypt certificate"
       17 $"Allow ssh login with passwords"
       18 $"Show firewall")
    # shellcheck disable=SC2068
    selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Security Settings" --menu $"Choose an operation, or ESC to exit:" 25 76 25 "${W[@]}" 3>&2 2>&1 1>&3)
    if [ ! "$selection" ]; then
        exit 0
    fi
    clear
    read_config_param SSL_CIPHERS
    read_config_param SSL_PROTOCOLS
    read_config_param SSH_CIPHERS
    read_config_param SSH_MACS
    read_config_param SSH_KEX
    get_imap_settings
    get_ssh_settings
    get_xmpp_settings
    import_settings
    export_settings
    case $selection in
        1)
            view_or_change_passwords
            exit 0;
            ;;
        2)
            clear
            echo $'Running STIG tests...'
            echo ''
            ${PROJECT_NAME}-tests --stig showall
            exit 0
            ;;
        3)
            clear
            echo $'Fixing any STIG failures...'
            echo ''
            ${PROJECT_NAME}-tests --stig fix
            echo $'Fixes applied. You will need to run the STIG tests again to be sure that they were all fixed.'
            exit 0
            ;;
        4)
            show_tripwire_verification_code
            any_key_verify
            exit 0
            ;;
        5)
            reset_tripwire
            exit 0
            ;;
        6)
            ping_enable_disable
            exit 0
            ;;
        7)
            dialog --title $"SSH host public keys" \
                   --msgbox "\\n$(get_ssh_server_key)" 12 60
            exit 0
            ;;
        8)
            menu_tor_bridges
            exit 0
            ;;
        9)
            store_passwords
            exit 0
            ;;
        10)
            export_passwords
            exit 0
            ;;
        11)
            regenerate_ssh_host_keys
            ;;
        12)
            regenerate_dh_keys
            ;;
        13)
            interactive_setup
            update_ciphersuite
            ;;
        14)
            create_letsencrypt
            ;;
        15)
            renew_letsencrypt
            ;;
        16)
            delete_letsencrypt
            ;;
        17)
            allow_ssh_passwords
            change_ssh_settings
            exit 0
            ;;
        18)
            show_firewall
            exit 0
            ;;
    esac
    change_website_settings
    change_imap_settings
    change_ssh_settings
    change_xmpp_settings
}
function import_settings {
    cd "$CURRENT_DIR" || exit 2468724684
    if [ ! $IMPORT_FILE ]; then
        return
    fi
    if [ ! -f $IMPORT_FILE ]; then
        echo $"Import file $IMPORT_FILE not found"
        exit 6393
    fi
    if grep -q "SSL_PROTOCOLS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSL_PROTOCOLS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            SSL_PROTOCOLS=$TEMP_VALUE
        fi
    fi
    if grep -q "SSL_CIPHERS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSL_CIPHERS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            SSL_CIPHERS=$TEMP_VALUE
        fi
    fi
    if grep -q "SSH_CIPHERS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSH_CIPHERS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            SSH_CIPHERS=$TEMP_VALUE
        fi
    fi
    if grep -q "SSH_MACS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSH_MACS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            SSH_MACS=$TEMP_VALUE
        fi
    fi
    if grep -q "SSH_KEX" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSH_KEX" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            SSH_KEX=$TEMP_VALUE
        fi
    fi
    if grep -q "SSH_HOST_KEY_ALGORITHMS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSH_HOST_KEY_ALGORITHMS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            SSH_HOST_KEY_ALGORITHMS=$TEMP_VALUE
        fi
    fi
    if grep -q "SSH_PASSWORDS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "SSH_PASSWORDS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [[ $TEMP_VALUE == "yes" || $TEMP_VALUE == "no" ]]; then
            SSH_PASSWORDS=$TEMP_VALUE
        fi
    fi
    if grep -q "XMPP_CIPHERS" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "XMPP_CIPHERS" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
            XMPP_CIPHERS=$TEMP_VALUE
        fi
    fi
    if grep -q "XMPP_ECC_CURVE" $IMPORT_FILE; then
        TEMP_VALUE=$(grep "XMPP_ECC_CURVE" $IMPORT_FILE | awk -F '=' '{print $2}')
        if [ ${#TEMP_VALUE} -gt 3 ]; then
            XMPP_ECC_CURVE=$TEMP_VALUE
        fi
    fi
}
function export_settings {
    if [ ! $EXPORT_FILE ]; then
        return
    fi
    cd "$CURRENT_DIR" || exit 92465274527
    if [ ! -f $EXPORT_FILE ]; then
        if [ "$SSL_PROTOCOLS" ]; then
            echo "SSL_PROTOCOLS=$SSL_PROTOCOLS" >> $EXPORT_FILE
        fi
        if [ "$SSL_CIPHERS" ]; then
            echo "SSL_CIPHERS=$SSL_CIPHERS" >> $EXPORT_FILE
        fi
        if [ "$SSH_CIPHERS" ]; then
            echo "SSH_CIPHERS=$SSH_CIPHERS" >> $EXPORT_FILE
        fi
        if [ "$SSH_MACS" ]; then
            echo "SSH_MACS=$SSH_MACS" >> $EXPORT_FILE
        fi
        if [ "$SSH_KEX" ]; then
            echo "SSH_KEX=$SSH_KEX" >> $EXPORT_FILE
        fi
        if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
            echo "SSH_HOST_KEY_ALGORITHMS=$SSH_HOST_KEY_ALGORITHMS" >> $EXPORT_FILE
        fi
        if [ "$SSH_PASSWORDS" ]; then
            echo "SSH_PASSWORDS=$SSH_PASSWORDS" >> $EXPORT_FILE
        fi
        if [ "$XMPP_CIPHERS" ]; then
            echo "XMPP_CIPHERS=$XMPP_CIPHERS" >> $EXPORT_FILE
        fi
        if [ "$XMPP_ECC_CURVE" ]; then
            echo "XMPP_ECC_CURVE=$XMPP_ECC_CURVE" >> $EXPORT_FILE
        fi
        echo "Security settings exported to $EXPORT_FILE"
        exit 0
    fi
    if [ "$SSL_PROTOCOLS" ]; then
        if grep -q "SSL_PROTOCOLS" $EXPORT_FILE; then
            sed -i "s|SSL_PROTOCOLS=.*|SSL_PROTOCOLS=$SSL_PROTOCOLS|g" $EXPORT_FILE
        else
            echo "SSL_PROTOCOLS=$SSL_PROTOCOLS" >> $EXPORT_FILE
        fi
    fi
    if [ "$SSL_CIPHERS" ]; then
        if grep -q "SSL_CIPHERS" $EXPORT_FILE; then
            sed -i "s|SSL_CIPHERS=.*|SSL_CIPHERS=$SSL_CIPHERS|g" $EXPORT_FILE
        else
            echo "SSL_CIPHERS=$SSL_CIPHERS" >> $EXPORT_FILE
        fi
    fi
    if [ "$SSH_CIPHERS" ]; then
        if grep -q "SSH_CIPHERS" $EXPORT_FILE; then
            sed -i "s|SSH_CIPHERS=.*|SSH_CIPHERS=$SSH_CIPHERS|g" $EXPORT_FILE
        else
            echo "SSH_CIPHERS=$SSH_CIPHERS" >> $EXPORT_FILE
        fi
    fi
    if [ "$SSH_MACS" ]; then
        if grep -q "SSH_MACS" $EXPORT_FILE; then
            sed -i "s|SSH_MACS=.*|SSH_MACS=$SSH_MACS|g" $EXPORT_FILE
        else
            echo "SSH_MACS=$SSH_MACS" >> $EXPORT_FILE
        fi
    fi
    if [ "$SSH_KEX" ]; then
        if grep -q "SSH_KEX" $EXPORT_FILE; then
            sed -i "s|SSH_KEX=.*|SSH_KEX=$SSH_KEX|g" $EXPORT_FILE
        else
            echo "SSH_KEX=$SSH_KEX" >> $EXPORT_FILE
        fi
    fi
    if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
        if grep -q "SSH_HOST_KEY_ALGORITHMS" $EXPORT_FILE; then
            sed -i "s|SSH_HOST_KEY_ALGORITHMS=.*|SSH_HOST_KEY_ALGORITHMS=$SSH_HOST_KEY_ALGORITHMS|g" $EXPORT_FILE
        else
            echo "SSH_HOST_KEY_ALGORITHMS=$SSH_HOST_KEY_ALGORITHMS" >> $EXPORT_FILE
        fi
    fi
    if [ "$SSH_PASSWORDS" ]; then
        if grep -q "SSH_PASSWORDS" $EXPORT_FILE; then
            sed -i "s|SSH_PASSWORDS=.*|SSH_PASSWORDS=$SSH_PASSWORDS|g" $EXPORT_FILE
        else
            echo "SSH_PASSWORDS=$SSH_PASSWORDS" >> $EXPORT_FILE
        fi
    fi
    if [ "$XMPP_CIPHERS" ]; then
        if grep -q "XMPP_CIPHERS" $EXPORT_FILE; then
            sed -i "s|XMPP_CIPHERS=.*|XMPP_CIPHERS=$XMPP_CIPHERS|g" $EXPORT_FILE
        else
            echo "XMPP_CIPHERS=$XMPP_CIPHERS" >> $EXPORT_FILE
        fi
    fi
    if [ "$XMPP_ECC_CURVE" ]; then
        if grep -q "XMPP_ECC_CURVE" $EXPORT_FILE; then
            sed -i "s|XMPP_ECC_CURVE=.*|XMPP_ECC_CURVE=$XMPP_ECC_CURVE|g" $EXPORT_FILE
        else
            echo "XMPP_ECC_CURVE=$XMPP_ECC_CURVE" >> $EXPORT_FILE
        fi
    fi
    echo $"Security settings exported to $EXPORT_FILE"
    exit 0
}
function refresh_gpg_keys {
    for d in /home/*/ ; do
        USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
        if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
            su -c 'gpg --refresh-keys' - "$USERNAME"
        fi
    done
    exit 0
}
function monkeysphere_sign_server_keys {
    server_keys_file=/home/$USER/.monkeysphere/server_keys
    if [ ! -f "$server_keys_file" ]; then
        exit 0
    fi
    keys_signed=
    while read -r line; do
        echo "$line"
        if [ ${#line} -gt 2 ]; then
            fpr=$(gpg --with-colons --fingerprint "$line" | grep fpr | head -n 1 | awk -F ':' '{print $10}')
            if [ ${#fpr} -gt 2 ]; then
                if gpg --sign-key "$fpr"; then
                    gpg --update-trustdb
                    keys_signed=1
                fi
            fi
        fi
    done <"$server_keys_file"
    if [ $keys_signed ]; then
        rm "$server_keys_file"
    fi
    exit 0
}
function htmly_hash {
    # produces a hash corresponding to a htmly password
    pass="$1"
    HTMLYHASH_FILENAME=/usr/bin/htmlyhash
    { echo '<?php';
      echo "parse_str(implode(\"&\", array_slice(\$argv, 1)), \$_GET);";
      echo "\$password = \$_GET[\"password\"];";
      echo "\$hash = password_hash(\$password, PASSWORD_BCRYPT);";
      echo "if (password_verify(\$password, \$hash)) {";
      echo "  echo \$hash;";
      echo '}';
      echo '?>'; } > "$HTMLYHASH_FILENAME"
    php $HTMLYHASH_FILENAME password="$pass"
}
function show_help {
    echo ''
    echo "${PROJECT_NAME}-sec"
    echo ''
    echo $'Alters the security settings'
    echo ''
    echo ''
    echo $'  -h --help                 Show help'
    echo $'  -e --export               Export security settings to a file'
    echo $'  -i --import               Import security settings from a file'
    echo $'  -r --refresh              Refresh GPG keys for all users'
    echo $'  -s --sign                 Sign monkeysphere server keys'
    echo $'     --register [domain]    Register a https domain with monkeysphere'
    echo $'  -b --htmlyhash [password] Returns the hash of a password for a htmly blog'
    echo ''
    exit 0
}
# Get the commandline options
while [ $# -gt 1 ]
do
    key="$1"
    case $key in
        -h|--help)
            show_help
            ;;
        # Export settings
        -e|--export)
            shift
            EXPORT_FILE="$1"
            ;;
        # Export settings
        -i|--import)
            shift
            IMPORT_FILE="$1"
            ;;
        # Refresh GPG keys
        -r|--refresh)
            shift
            refresh_gpg_keys
            ;;
        # register a website
        --register|--reg|--site)
            shift
            register_website "$1"
            ;;
        # user signs monkeysphere server keys
        -s|--sign)
            shift
            monkeysphere_sign_server_keys
            ;;
        # get a hash of the given htmly password
        -b|--htmlyhash)
            shift
            htmly_hash "$1"
            exit 0
            ;;
        *)
            # unknown option
            ;;
    esac
    shift
done
menu_security_settings
exit 0
 |