reworked genpwd / introduce pwdscore
This commit is contained in:
@@ -63,6 +63,7 @@ help()
|
|||||||
printf "ppn\t\tDisplay process matching the exact process name given in parameter\n"
|
printf "ppn\t\tDisplay process matching the exact process name given in parameter\n"
|
||||||
printf "ppu\t\tDisplay processes owned by the given user\n"
|
printf "ppu\t\tDisplay processes owned by the given user\n"
|
||||||
printf "profile_upgrade\tUpgrade profile to the latest version\n"
|
printf "profile_upgrade\tUpgrade profile to the latest version\n"
|
||||||
|
printf "pwdscore\tCalculate password strength score\n"
|
||||||
printf "rain\t\tLet the rain fall\n"
|
printf "rain\t\tLet the rain fall\n"
|
||||||
printf "rmhost\t\tRemove host (IP and/or DNS name) from current known_hosts\n"
|
printf "rmhost\t\tRemove host (IP and/or DNS name) from current known_hosts\n"
|
||||||
printf "rmspc\t\tRemove spaces from file and directory names\n"
|
printf "rmspc\t\tRemove spaces from file and directory names\n"
|
||||||
|
|||||||
349
profile.d/pwd.sh
349
profile.d/pwd.sh
@@ -53,21 +53,22 @@
|
|||||||
genpwd()
|
genpwd()
|
||||||
{
|
{
|
||||||
local length=16
|
local length=16
|
||||||
local occurs=2 # Bug, if set to 1, seems to be ignored
|
local occurs=2
|
||||||
local symb=1 maj=1 min=1 numb=1
|
local symb=1 maj=1 min=1 numb=1
|
||||||
local nbpwd=1
|
local nbpwd=1
|
||||||
local extcar
|
local extcar=""
|
||||||
|
|
||||||
local PARSED
|
local PARSED
|
||||||
PARSED=$(getopt -o hsnu l e:L:o: --long \
|
PARSED=$(getopt -o hsnule:L:o: --long \
|
||||||
help,nosymbols,nonumbers,noup,nolow,extracars:,length:,occurences: -n 'genpwd' -- "$@")
|
help,nosymbols,nonumbers,noup,nolow,extracars:,length:,occurences:,occurrences: \
|
||||||
|
-n 'genpwd' -- "$@")
|
||||||
if [[ $? -ne 0 ]]; then return 1; fi
|
if [[ $? -ne 0 ]]; then return 1; fi
|
||||||
eval set -- "$PARSED"
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-h|--help)
|
-h|--help)
|
||||||
printf "genpwd: Generate secure random password(s).\n\n"
|
printf "genpwd: Generate random password(s).\n\n"
|
||||||
printf "Usage: genpwd [options] [nb_passwd]\n\n"
|
printf "Usage: genpwd [options] [nb_passwd]\n\n"
|
||||||
printf "Options:\n"
|
printf "Options:\n"
|
||||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
@@ -75,9 +76,9 @@ local PARSED
|
|||||||
printf "\t-n, --nonumbers\t\tExclude numbers\n"
|
printf "\t-n, --nonumbers\t\tExclude numbers\n"
|
||||||
printf "\t-u, --noup\t\tExclude uppercase letters\n"
|
printf "\t-u, --noup\t\tExclude uppercase letters\n"
|
||||||
printf "\t-l, --nolow\t\tExclude lowercase letters\n"
|
printf "\t-l, --nolow\t\tExclude lowercase letters\n"
|
||||||
printf "\t-e, --extracars <c>\tAdd characters to list\n"
|
printf "\t-e, --extracars <c>\tAdd characters to the pool\n"
|
||||||
printf "\t-L, --length <n>\tSet password length (default: 16)\n"
|
printf "\t-L, --length <n>\tSet password length (default: 16)\n"
|
||||||
printf "\t-o, --occurences <n>\tMax occurences per character (default: 2)\n"
|
printf "\t-o, --occurences <n>\tMax occurrences per character (default: 2)\n"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
-s|--nosymbols)
|
-s|--nosymbols)
|
||||||
@@ -102,102 +103,306 @@ local PARSED
|
|||||||
;;
|
;;
|
||||||
-L|--length)
|
-L|--length)
|
||||||
length="$2"
|
length="$2"
|
||||||
if ! [[ $length =~ ^[0-9]+$ ]]; then
|
if ! [[ $length =~ ^[1-9][0-9]*$ ]]; then
|
||||||
disp E "The --length parameter requires a number."
|
disp E "The --length parameter requires a positive integer."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
-o|--occurences)
|
-o|--occurences|--occurrences)
|
||||||
occurs="$2"
|
occurs="$2"
|
||||||
if ! [[ $occurs =~ ^[1-9]+$ ]]; then
|
if ! [[ $occurs =~ ^[1-9][0-9]*$ ]]; then
|
||||||
disp E "The --occurs parameter requires a number from 1 to 9."
|
disp E "The --occurences parameter requires a positive integer."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
shift; break
|
shift
|
||||||
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
break
|
disp E "Invalid options, use \"genpwd --help\" to display usage."
|
||||||
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ -n "$1" ]]; then
|
if [[ $# -gt 1 ]]; then
|
||||||
|
disp E "Too many positional arguments. Use only [nb_passwd]."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# -eq 1 ]]; then
|
||||||
nbpwd="$1"
|
nbpwd="$1"
|
||||||
if ! [[ $nbpwd =~ ^[0-9]+$ ]]; then
|
if ! [[ $nbpwd =~ ^[1-9][0-9]*$ ]]; then
|
||||||
disp E "The number of password to generate must be a number."
|
disp E "The number of passwords to generate must be a positive integer."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Function selecting a random caracter from the list in parameter
|
local carset=""
|
||||||
pickcar() {
|
local unique_carset=""
|
||||||
# When a character is picked we check if it's not appearing already twice
|
local ch=""
|
||||||
# elsewhere, we choose an other char, to compensate weak bash randomizer
|
local i=0
|
||||||
while [[ -z $char ]]; do
|
local n=0
|
||||||
local char="${1:RANDOM%${#1}:1} $RANDOM"
|
local idx=0
|
||||||
if [[ $(awk -F"$char" '{print NF-1}' <<<"$picked") -gt $occurs ]]; then
|
local attempts=0
|
||||||
unset char
|
local count=0
|
||||||
fi
|
local max_attempts=0
|
||||||
done
|
local set=""
|
||||||
picked+="$char"
|
local char=""
|
||||||
echo "$char"
|
local -a required_sets=()
|
||||||
|
declare -A seen_chars=()
|
||||||
|
|
||||||
|
(( symb )) && {
|
||||||
|
required_sets+=('!.@#&%/^-_')
|
||||||
|
carset+='!.@#&%/^-_'
|
||||||
}
|
}
|
||||||
|
(( numb )) && {
|
||||||
|
required_sets+=('0123456789')
|
||||||
|
carset+='0123456789'
|
||||||
|
}
|
||||||
|
(( maj )) && {
|
||||||
|
required_sets+=('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||||
|
carset+='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
}
|
||||||
|
(( min )) && {
|
||||||
|
required_sets+=('abcdefghijklmnopqrstuvwxyz')
|
||||||
|
carset+='abcdefghijklmnopqrstuvwxyz'
|
||||||
|
}
|
||||||
|
if [[ -n $extcar ]]; then
|
||||||
|
required_sets+=("$extcar")
|
||||||
|
carset+="$extcar"
|
||||||
|
fi
|
||||||
|
|
||||||
disp I "Generating $nbpwd passwords, please wait..."
|
if [[ -z $carset ]]; then
|
||||||
|
disp E "No characters are available. Re-enable at least one character class."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for (( i=0; i<${#carset}; i++ )); do
|
||||||
|
ch=${carset:i:1}
|
||||||
|
if [[ -z ${seen_chars["$ch"]+x} ]]; then
|
||||||
|
seen_chars["$ch"]=1
|
||||||
|
unique_carset+="$ch"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
unset seen_chars
|
||||||
|
carset="$unique_carset"
|
||||||
|
|
||||||
|
if (( ${#required_sets[@]} > length )); then
|
||||||
|
disp E "The selected character classes require a longer password."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( length > ${#carset} * occurs )); then
|
||||||
|
disp E "The occurrence limit is too strict for the selected length."
|
||||||
|
disp E "Please allow more characters or increase --occurences."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Generating $nbpwd password(s), please wait..."
|
||||||
for (( n=1; n<=nbpwd; n++ )); do
|
for (( n=1; n<=nbpwd; n++ )); do
|
||||||
{
|
local -a password_chars=()
|
||||||
local carset='' # store final caracter set to use
|
local -A char_count=()
|
||||||
local picked='' # store already used caracter
|
max_attempts=$(( ${#carset} * (occurs + 1) + 32 ))
|
||||||
local rlength=0 # store already assigned length of caracters
|
|
||||||
|
|
||||||
# ?, *, $ and \ impossible to use to my knowledge as it would be interpreted
|
for set in "${required_sets[@]}"; do
|
||||||
if [[ $symb == 1 ]]; then
|
attempts=0
|
||||||
pickcar '!.@#&%/^-_'
|
while :; do
|
||||||
carset+='!.@#&%/^-_'
|
if (( attempts >= max_attempts )); then
|
||||||
((rlength++))
|
disp E "Unable to satisfy the occurrence limit with the current settings."
|
||||||
fi
|
return 1
|
||||||
if [[ $numb == 1 ]]; then
|
fi
|
||||||
pickcar '0123456789'
|
|
||||||
carset+='0123456789'
|
|
||||||
((rlength++))
|
|
||||||
fi
|
|
||||||
if [[ $maj == 1 ]]; then
|
|
||||||
pickcar 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
||||||
carset+='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
||||||
((rlength++))
|
|
||||||
fi
|
|
||||||
if [[ $min == 1 ]]; then
|
|
||||||
pickcar 'abcdefghijklmnopqrstuvwxyz'
|
|
||||||
carset+='abcdefghijklmnopqrstuvwxyz'
|
|
||||||
((rlength++))
|
|
||||||
fi
|
|
||||||
if [[ -n $extcar ]]; then
|
|
||||||
pickcar "$extcar"
|
|
||||||
carset+=$extcar
|
|
||||||
((rlength++))
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if we have enough car to have something viable
|
idx=$(( RANDOM % ${#set} ))
|
||||||
if [[ ${#carset} -lt $length ]]; then
|
char=${set:idx:1}
|
||||||
disp E 'Not enought caracters are authorised for the password length.'
|
count=${char_count["$char"]:-0}
|
||||||
disp E 'Please allow more caracter (preferably) or reduce password lentgh.'
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
for i in $(seq 1 $(($length - $rlength))); do
|
if (( count < occurs )); then
|
||||||
pickcar "$carset"
|
char_count["$char"]=$(( count + 1 ))
|
||||||
|
password_chars+=("$char")
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
((attempts++))
|
||||||
done
|
done
|
||||||
} | sort -R | awk '{printf "%s", $1}'
|
done
|
||||||
unset picked carset rlength
|
|
||||||
echo
|
while (( ${#password_chars[@]} < length )); do
|
||||||
|
attempts=0
|
||||||
|
while :; do
|
||||||
|
if (( attempts >= max_attempts )); then
|
||||||
|
disp E "Unable to satisfy the occurrence limit with the current settings."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
idx=$(( RANDOM % ${#carset} ))
|
||||||
|
char=${carset:idx:1}
|
||||||
|
count=${char_count["$char"]:-0}
|
||||||
|
|
||||||
|
if (( count < occurs )); then
|
||||||
|
char_count["$char"]=$(( count + 1 ))
|
||||||
|
password_chars+=("$char")
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
((attempts++))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
for (( i=${#password_chars[@]} - 1; i>0; i-- )); do
|
||||||
|
idx=$(( RANDOM % (i + 1) ))
|
||||||
|
char=${password_chars[i]}
|
||||||
|
password_chars[i]=${password_chars[idx]}
|
||||||
|
password_chars[idx]=$char
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s' "${password_chars[@]}"
|
||||||
|
printf '\n'
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
export -f genpwd
|
export -f genpwd
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# pwdscore : score a password quality from 1 to 100
|
||||||
|
# Usage: pwdscore [options] <password>
|
||||||
|
pwdscore()
|
||||||
|
{
|
||||||
|
local verbose=0
|
||||||
|
local PARSED
|
||||||
|
|
||||||
|
PARSED=$(getopt -o hv --long help,verbose -n 'pwdscore' -- "$@")
|
||||||
|
if [[ $? -ne 0 ]]; then return 1; fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "pwdscore: Score a password from 1 to 100.\n\n"
|
||||||
|
printf "Usage: pwdscore [options] <password>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-v, --verbose\t\tShow details about the computed score\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-v|--verbose)
|
||||||
|
verbose=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"pwdscore --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $# -ne 1 ]] && {
|
||||||
|
disp E "Please provide exactly one password to score."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local password="$1"
|
||||||
|
local lower=${password,,}
|
||||||
|
local length=${#password}
|
||||||
|
local score=0
|
||||||
|
local rating="very weak"
|
||||||
|
local unique_count=0
|
||||||
|
local i=0 idx=0
|
||||||
|
local c1=0 c2=0 c3=0
|
||||||
|
local ch=""
|
||||||
|
local has_lower=0 has_upper=0 has_digit=0 has_symbol=0
|
||||||
|
local -A seen=()
|
||||||
|
|
||||||
|
if [[ -z $password ]]; then
|
||||||
|
printf '1\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( length >= 20 )); then
|
||||||
|
score=40
|
||||||
|
elif (( length >= 16 )); then
|
||||||
|
score=34
|
||||||
|
elif (( length >= 12 )); then
|
||||||
|
score=28
|
||||||
|
elif (( length >= 8 )); then
|
||||||
|
score=18
|
||||||
|
else
|
||||||
|
score=$(( length * 2 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ $password =~ [a-z] ]] && { has_lower=1; ((score += 12)); }
|
||||||
|
[[ $password =~ [A-Z] ]] && { has_upper=1; ((score += 12)); }
|
||||||
|
[[ $password =~ [0-9] ]] && { has_digit=1; ((score += 12)); }
|
||||||
|
[[ $password =~ [^[:alnum:]] ]] && { has_symbol=1; ((score += 14)); }
|
||||||
|
|
||||||
|
for (( i=0; i<length; i++ )); do
|
||||||
|
ch=${password:i:1}
|
||||||
|
if [[ -z ${seen["$ch"]+x} ]]; then
|
||||||
|
seen["$ch"]=1
|
||||||
|
((unique_count++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
(( score += (unique_count * 10) / length ))
|
||||||
|
|
||||||
|
if [[ $lower =~ (password|admin|root|qwerty|azerty|welcome|letmein|secret|changeme) ]]; then
|
||||||
|
((score -= 25))
|
||||||
|
fi
|
||||||
|
if [[ $lower =~ (1234|abcd|qwer|0000|1111|aaaa) ]]; then
|
||||||
|
((score -= 15))
|
||||||
|
fi
|
||||||
|
[[ $password =~ (.)\1\1 ]] && ((score -= 10))
|
||||||
|
(( length < 8 )) && ((score -= 10))
|
||||||
|
(( unique_count * 2 < length )) && ((score -= 10))
|
||||||
|
|
||||||
|
for (( idx=0; idx<length-2; idx++ )); do
|
||||||
|
printf -v c1 '%d' "'${lower:idx:1}"
|
||||||
|
printf -v c2 '%d' "'${lower:idx+1:1}"
|
||||||
|
printf -v c3 '%d' "'${lower:idx+2:1}"
|
||||||
|
if (( (c2 == c1 + 1 && c3 == c2 + 1) || \
|
||||||
|
(c2 == c1 - 1 && c3 == c2 - 1) )); then
|
||||||
|
((score -= 10))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
(( score < 1 )) && score=1
|
||||||
|
(( score > 100 )) && score=100
|
||||||
|
|
||||||
|
if (( score >= 90 )); then
|
||||||
|
rating='excellent'
|
||||||
|
elif (( score >= 75 )); then
|
||||||
|
rating='strong'
|
||||||
|
elif (( score >= 60 )); then
|
||||||
|
rating='good'
|
||||||
|
elif (( score >= 40 )); then
|
||||||
|
rating='fair'
|
||||||
|
elif (( score >= 20 )); then
|
||||||
|
rating='weak'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( verbose )); then
|
||||||
|
printf 'Score: %d/100\n' "$score"
|
||||||
|
printf 'Rating: %s\n' "$rating"
|
||||||
|
printf 'Length: %d\n' "$length"
|
||||||
|
printf 'Lowercase: %s\n' "$([[ $has_lower -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Uppercase: %s\n' "$([[ $has_upper -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Digits: %s\n' "$([[ $has_digit -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Symbols: %s\n' "$([[ $has_symbol -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Unique chars: %d\n' "$unique_count"
|
||||||
|
else
|
||||||
|
printf '%d\n' "$score"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f pwdscore
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# EOF
|
# EOF
|
||||||
|
|||||||
Reference in New Issue
Block a user