From 450d514a0c12aa5ed603a0603c8d30ee1202a0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC=D0=B8=D0=B9?= Date: Sun, 20 Jun 2021 00:36:51 +0300 Subject: [PATCH] Add cli frontend for pw, add. error handling --- DESTDIR/etc/security/pwquality.conf | 10 ++- .../pwquality/back_pwquality.sh | 47 ++---------- back_pwquality.sh | 45 ++--------- common.sh | 2 +- front_pwquality.sh | 14 ++-- front_pwquality_cli.sh | 75 +++++++++++++++++++ 6 files changed, 102 insertions(+), 91 deletions(-) create mode 100644 front_pwquality_cli.sh diff --git a/DESTDIR/etc/security/pwquality.conf b/DESTDIR/etc/security/pwquality.conf index 6dcdb2b..babe7db 100644 --- a/DESTDIR/etc/security/pwquality.conf +++ b/DESTDIR/etc/security/pwquality.conf @@ -1,15 +1,17 @@ -minlen = 8 +difok = 99 +minlen = 7 dcredit = 0 -ucredir = 0 +ucredit = 0 lcredit = 0 ocredit = 0 minclass = 0 maxrepeat = 0 maxsequence = 0 maxclassrepeat = 0 -gecoscheck = 0 +gecoscheck = FALSE dictcheck = 1 -usercheck = 1 +usercheck = 0 usersubstr = 0 enforcing = 1 retry = 1 +enforce_for_root diff --git a/DESTDIR/usr/share/linux-infosec-setupper/pwquality/back_pwquality.sh b/DESTDIR/usr/share/linux-infosec-setupper/pwquality/back_pwquality.sh index 2ec48d6..f4212a7 100644 --- a/DESTDIR/usr/share/linux-infosec-setupper/pwquality/back_pwquality.sh +++ b/DESTDIR/usr/share/linux-infosec-setupper/pwquality/back_pwquality.sh @@ -1,128 +1,94 @@ #!/bin/bash set -e -source "${SHARE_DIR_ROOT}/common.sh" +source "${DESTDIR}/usr/share/linux-infosec-setupper/common.sh" +source "${SHARE_DIR_PWQUALITY}/parse_pwquality.sh" _mk_pwquality_conf() { - local failed=0 - local difok=1 \ - minlen=8 \ - dcredit=0 \ - ucredit=0 \ - lcredit=0 \ - ocredit=0 \ - minclass=0 \ - maxrepeat=0 \ - maxsequence=0 \ - maxclassrepeat=0 \ - gecoscheck=0 \ - dictcheck=1 \ - usercheck=1 \ - usersubstr=0 \ - enforcing=1 \ - badwords \ - dictpath \ - retry=1 \ - enforce_for_root=0 \ - local_users_only=0 +local failed=0 +while read -r line; do local "$line" || { error $"Unable to parse /etc/security/pwquality.conf correctly; execute \n%s" "rm ${VAR_DIR_PWQUALITY}/pw_changed"; exit 1; }; done < <(_pw_parse_conf) while [ -n "$1" ]; do case "$1" in --difok) shift; _check_argument_is_number "$1" "--difok" || failed=1 difok="$1" - shift ;; --minlen) shift; _check_argument_value "$1" "6" "--minlen" || failed=1 minlen="$1" - shift ;; --dcredit) shift; _check_argument_is_number "$1" "--dcredit" "-" || failed=1 dcredit="$1" - shift ;; --ucredit) shift; _check_argument_is_number "$1" "--ucredit" "-" || failed=1 ucredit="$1" - shift ;; --lcredit) shift; _check_argument_is_number "$1" "--lcredit" "-" || failed=1 lcredit="$1" - shift ;; --ocredit) shift; _check_argument_is_number "$1" "--ocredit" "-" || failed=1 ocredit="$1" - shift ;; --minclass) shift; _check_argument_is_number "$1" "--minclass" || failed=1 minclass="$1" - shift ;; --maxrepeat) shift; _check_argument_is_number "$1" "--maxrepeat" || failed=1 maxrepeat="$1" - shift ;; --maxsequence) shift; _check_argument_is_number "$1" "--maxsequence" || failed=1 maxsequence="$1" - shift ;; --maxclassrepeat) shift; _check_argument_is_number "$1" "--maxclassrepeat" || failed=1 maxclassrepeat="$1" - shift ;; --gecoscheck) shift; _check_argument_is_number "$1" "--gecoscheck" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } geoscheck="$1" - shift ;; --dictcheck) shift; _check_argument_is_number "$1" "--dictcheck" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } dictcheck="$1" - shift ;; --usercheck) shift; _check_argument_is_number "$1" "--usercheck" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } usercheck="$1" - shift ;; --usersubstr) shift; _check_argument_is_number "$1" "--usersubstr" || failed=1 usersubstr="$1" - shift ;; --enforcing) shift; _check_argument_is_number "$1" "--enforcing" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } enforcing="$1" - shift ;; --retry) shift; _check_argument_is_number "$1" "--retry" || failed=1 - shift + retry="$1" ;; --enforce_for_root) shift; _check_argument_is_number "$1" "--enforce_for_root" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } enforce_for_root="$1" - shift ;; --local_users_only) shift; _check_argument_is_number "$1" "--local_users_only" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } local_users_only="$1" - shift ;; esac + shift done if [ "$failed" != 0 ]; then return 1 @@ -145,6 +111,7 @@ usersubstr = $usersubstr enforcing = $enforcing retry = $retry EOF +# These parameters do not have keys (numbers after the = sign), so we work with them in a different way if [ "$enforce_for_root" == 1 ]; then echo "enforce_for_root"; fi if [ "$local_users_only" == 1 ]; then echo "local_users_only"; fi } diff --git a/back_pwquality.sh b/back_pwquality.sh index af2bdd3..f4212a7 100644 --- a/back_pwquality.sh +++ b/back_pwquality.sh @@ -2,127 +2,93 @@ set -e source "${DESTDIR}/usr/share/linux-infosec-setupper/common.sh" +source "${SHARE_DIR_PWQUALITY}/parse_pwquality.sh" _mk_pwquality_conf() { - local failed=0 - local difok=1 \ - minlen=8 \ - dcredit=0 \ - ucredit=0 \ - lcredit=0 \ - ocredit=0 \ - minclass=0 \ - maxrepeat=0 \ - maxsequence=0 \ - maxclassrepeat=0 \ - gecoscheck=0 \ - dictcheck=1 \ - usercheck=1 \ - usersubstr=0 \ - enforcing=1 \ - badwords \ - dictpath \ - retry=1 \ - enforce_for_root=0 \ - local_users_only=0 +local failed=0 +while read -r line; do local "$line" || { error $"Unable to parse /etc/security/pwquality.conf correctly; execute \n%s" "rm ${VAR_DIR_PWQUALITY}/pw_changed"; exit 1; }; done < <(_pw_parse_conf) while [ -n "$1" ]; do case "$1" in --difok) shift; _check_argument_is_number "$1" "--difok" || failed=1 difok="$1" - shift ;; --minlen) shift; _check_argument_value "$1" "6" "--minlen" || failed=1 minlen="$1" - shift ;; --dcredit) shift; _check_argument_is_number "$1" "--dcredit" "-" || failed=1 dcredit="$1" - shift ;; --ucredit) shift; _check_argument_is_number "$1" "--ucredit" "-" || failed=1 ucredit="$1" - shift ;; --lcredit) shift; _check_argument_is_number "$1" "--lcredit" "-" || failed=1 lcredit="$1" - shift ;; --ocredit) shift; _check_argument_is_number "$1" "--ocredit" "-" || failed=1 ocredit="$1" - shift ;; --minclass) shift; _check_argument_is_number "$1" "--minclass" || failed=1 minclass="$1" - shift ;; --maxrepeat) shift; _check_argument_is_number "$1" "--maxrepeat" || failed=1 maxrepeat="$1" - shift ;; --maxsequence) shift; _check_argument_is_number "$1" "--maxsequence" || failed=1 maxsequence="$1" - shift ;; --maxclassrepeat) shift; _check_argument_is_number "$1" "--maxclassrepeat" || failed=1 maxclassrepeat="$1" - shift ;; --gecoscheck) shift; _check_argument_is_number "$1" "--gecoscheck" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } geoscheck="$1" - shift ;; --dictcheck) shift; _check_argument_is_number "$1" "--dictcheck" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } dictcheck="$1" - shift ;; --usercheck) shift; _check_argument_is_number "$1" "--usercheck" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } usercheck="$1" - shift ;; --usersubstr) shift; _check_argument_is_number "$1" "--usersubstr" || failed=1 usersubstr="$1" - shift ;; --enforcing) shift; _check_argument_is_number "$1" "--enforcing" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } enforcing="$1" - shift ;; --retry) shift; _check_argument_is_number "$1" "--retry" || failed=1 - shift + retry="$1" ;; --enforce_for_root) shift; _check_argument_is_number "$1" "--enforce_for_root" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } enforce_for_root="$1" - shift ;; --local_users_only) shift; _check_argument_is_number "$1" "--local_users_only" || failed=1 [[ "$1" =~ (0|1) ]] || { error $"The received parameters are not correct. Expected %s, received %s" $"0 or 1" "$1"; failed=1; } local_users_only="$1" - shift ;; esac + shift done if [ "$failed" != 0 ]; then return 1 @@ -149,4 +115,3 @@ EOF if [ "$enforce_for_root" == 1 ]; then echo "enforce_for_root"; fi if [ "$local_users_only" == 1 ]; then echo "local_users_only"; fi } -_mk_pwquality_conf --gecoscheck 3 diff --git a/common.sh b/common.sh index d94dacd..8b6baad 100644 --- a/common.sh +++ b/common.sh @@ -17,7 +17,7 @@ AUDIT_DAEMON_SYSTEMD_OVERRIDE="${DESTDIR}/etc/systemd/system/auditd.service.d/90 REGEX_EMAIL="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$" error() { - printf "$@" 1>&2 + printf -- "$@" 1>&2 echo '' 1>&2 } diff --git a/front_pwquality.sh b/front_pwquality.sh index ba4e8f3..7beaaac 100644 --- a/front_pwquality.sh +++ b/front_pwquality.sh @@ -2,12 +2,14 @@ source "${DESTDIR}/usr/share/linux-infosec-setupper/common.sh" +PWQUALITY_FRONT=1 + # Check whether we are running the script for the first time # Since the config may be standard from the package, it may not be parsed correctly. # We write our default config instead of the original one, so that the parsing works correctly if ! [[ -f "${VAR_DIR_PWQUALITY}/pw_changed" ]]; then - cat "${SHARE_DIR_PWQUALITY}/pw_default" > "${DESTDIR}/etc/security/pwquality.conf" - install -D -m 444 /dev/null "${VAR_DIR_PWQUALITY}/pw_changed" + cat "${SHARE_DIR_PWQUALITY}/pw_default" > "${DESTDIR}/etc/security/pwquality.conf" || { error $"Unable to write to file %s" "${DESTDIR}/etc/security/pwquality.conf"; exit 1; } + install -D -m 444 /dev/null "${VAR_DIR_PWQUALITY}/pw_changed" || { error $"Unable to write to file %s" "${VAR_DIR_PWQUALITY}/pw_changed"; exit 1; } fi source "${SHARE_DIR_PWQUALITY}/parse_pwquality.sh" @@ -19,13 +21,13 @@ while read -r line; do declare "$line" || { error $"Unable to parse /etc/securit # We change the following parameters 0 to FALSE and 1 to TRUE for i in gecoscheck enforce_for_root local_users_only dictcheck usercheck enforcing; do # The variables have the same name as the lines in the config - eval 'if [[ $'$i' == 1 ]]; then declare $i=TRUE; else declare $i=FALSE; fi' + eval 'if [[ $'$i' == 1 ]]; then declare $i=TRUE; else declare $i=FALSE; fi' || { error $"Unable to set variable %s" "$i"; exit 1; } done var="$(yad --title=$"linux-infosec-setupper" --form \ --text-align=center \ --bool-fmt=T \ --text=$"Password policies setup" \ - --image=/usr/share/icons/hicolor/48x48/apps/gcr-key.png \ + --image=gcr-key \ --scroll \ --width=800 \ --height=800 \ @@ -71,7 +73,7 @@ var="$(yad --title=$"linux-infosec-setupper" --form \ # If we clicked on the "Load default" button, we decided to restore the settings. # The exit code after clicking on this button is 3. We restore the config if we clicked on this button if [ "$_status" == 3 ]; then - cat "${SHARE_DIR_PWQUALITY}/pw_default" > "${DESTDIR}/etc/security/pwquality.conf" + cat "${SHARE_DIR_PWQUALITY}/pw_default" > "${DESTDIR}/etc/security/pwquality.conf" || { error $"Unable to write to file %s" "${DESTDIR}/etc/security/pwquality.conf"; exit 1; } fi # If we decide to undo the changes and not change anything, the var variable will be empty. @@ -103,4 +105,4 @@ done <<<"$var" | sed '/^$/d' | \ ;18s/^/--local_users_only /' | tr '\n' ' ')" source "${SHARE_DIR_PWQUALITY}/back_pwquality.sh" -_mk_pwquality_conf $var2 > "${DESTDIR}/etc/security/pwquality.conf" +_mk_pwquality_conf $var2 > "${DESTDIR}/etc/security/pwquality.conf" || { error $"Unable to write to file %s" "${DESTDIR}/etc/security/pwquality.conf"; exit 1; } diff --git a/front_pwquality_cli.sh b/front_pwquality_cli.sh new file mode 100644 index 0000000..bf9b558 --- /dev/null +++ b/front_pwquality_cli.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +source "${DESTDIR}/usr/share/linux-infosec-setupper/common.sh" + +# Check whether we are running the script for the first time +# Since the config may be standard from the package, it may not be parsed correctly. +# We write our default config instead of the original one, so that the parsing works correctly +if ! [[ -f "${VAR_DIR_PWQUALITY}/pw_changed" ]]; then + cat "${SHARE_DIR_PWQUALITY}/pw_default" > "${DESTDIR}/etc/security/pwquality.conf" || { error $"Unable to write to file %s" "${DESTDIR}/etc/security/pwquality.conf"; exit 1; } + install -D -m 444 /dev/null "${VAR_DIR_PWQUALITY}/pw_changed" || { error $"Unable to write to file %s" "${VAR_DIR_PWQUALITY}/pw_changed"; exit 1; } +fi + +source "${SHARE_DIR_PWQUALITY}/parse_pwquality.sh" +source "${SHARE_DIR_PWQUALITY}/back_pwquality.sh" + +while read -r line; do declare "$line" || { error $"Unable to parse /etc/security/pwquality.conf correctly; execute \n%s" "rm ${VAR_DIR_PWQUALITY}/pw_changed"; exit 1; }; done < <(_pw_parse_conf) + +PWQUALITY_FRONT=1 +failed=0 +_args="$(echo "$@" | sed 's/\(d \|-d \)/--difok / + ;s/\(m \|-m \)/--minlen / + ;s/\(dc \|-dc \)/--dcredit / + ;s/\(uc \|-uc \)/--ucredit / + ;s/\(lc \|-lc \)/--lcredit / + ;s/\(oc \|-oc \)/--ocredit / + ;s/\(geco \|-geco \)/--gecoscheck / + ;s/\(e \|-e \)/--enforcing / + ;s/\(r \|-r \)/--retry /')" + +if [ -z "$1" ]; then + error $"No arguments specified" + exit 1 +fi + +case "$1" in -h|--help|h|help) +echo $"Usage: #NAME# --[OPTIONS...]" +echo $"#NAME# allows you to manage the file configuration for pwquality in the cli option. A GUI version is also available: #NAME2#" +echo '' +echo $" Options:" +echo $" d, difok Number of characters in the new password that must not be present in the old password" +echo $" m, minlen Minimum acceptable size for the new password" +echo $" dc, dcredit The maximum credit for having digits in the new password" +echo $" uc, ucredit The maximum credit for having uppercase characters in the new password" +echo $" lc, lcredit The maximum credit for having lowercase characters in the new password" +echo $" oc, ocredit The maximum credit for having other characters in the new password" +echo $" minclass The minimum number of required classes of characters for the new password" +echo $" maxrepeat The maximum number of allowed same consecutive characters in the new password" +echo $" maxsequence The maximum length of monotonic character sequences in the new password" +echo $" maxclassrepeat The maximum number of allowed consecutive characters of the same class in the new password" +echo $" geco, gecoscheck Check whether the words longer than 3 characters from the GECOS field of the user's passwd(5) entry are contained in the new password" +echo $" dictcheck Check whether the password (with possible modifications) matches a word in a dictionary" +echo $" usercheck Check whether the password (with possible modifications) contains the user name in some form" +echo $" usersubstr Check whether the password contains a substring of at least N length in some form" +echo $" enforcing Reject the password if it fails the checks, otherwise only print the warning" +echo $" retry Prompt user at most N times before returning with error" +echo $" enforce_for_root The module will return error on failed check even if the user changing the password is root" +echo $" local_users_only The module will not test the password quality for users that are not present in the /etc/passwd file" +echo '' +exit 0 + ;; + esac + shift +_number=1 +while read -r args; do + if [[ $(( _number % 2 )) = 1 ]]; then + case "$args" in + --*) : ;; + -*) args="-$args" ;; + *) args="--$args" ;; + esac + fi + arg_line+=("$args") + ((_number++)) +done < <(echo -e "${_args// /\\n}") +_mk_pwquality_conf ${arg_line[@]} > "${DESTDIR}/etc/security/pwquality.conf" || { error $"Unable to write to file %s" "${DESTDIR}/etc/security/pwquality.conf"; exit 1; }