summaryrefslogtreecommitdiff
path: root/security/pwned-check/files/pwned-check.sh.in
blob: bf4886f9783394832cd9af963d6680af62e54e25 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/bin/sh
#
# Copyright (c) 2017 by Stefan Esser <se@freebsd.org>
# All rights reserved.
#
# Distributed under the BSD 2-clause Simplified License.
#

CFGFILE="%%PREFIX%%/etc/pwned-check.conf"

[ -r "$CFGFILE" ] && . $CFGFILE
: ${DBDIR:=/var/db/pwned-check}
: ${URLBASE:=https://downloads.pwnedpasswords.com/passwords}

# Helper functions
progname ()
{
    basename "$0"
}

errexit ()
{
    echo $(progname)": $@"
    exit 1
}

usage ()
{
    echo "usage: "$(progname)" [-u]"
    exit 2
}

# Fetch files with pwned password hashes
fetchpwfiles ()
{
	umask 022
	mkdir -p $DBDIR || errexit "No write permission on data directory."
	local f s_txt s_txt_7z hash
	while read f s_txt s_txt_7z hash
	do
		local f7z="$f.7z"
		echo "Checking '$DBDIR/$f' ..."
		local s_txt_is=$(stat -f %z $f 2>/dev/null)
		if [ "$s_txt_is" != "$s_txt" ]; then
			local s_txt_7z_is=$(stat -f %z $f7z 2>/dev/null)
			if [ "$s_txt_7z_is" != "$s_txt_7z" ]; then
				echo "Fetching '$DBDIR/$f7z' ..."
				fetch -S $s_txt_7z "$URLBASE/$f7z" || errexit "Could not fetch '$URLBASE/$f7z'."
			fi
			echo "Checking '$DBDIR/$f7z' ..."
			local hash_is=$(sha1 -q "$f7z")
			if [ "$hash_is" != "$hash" ]; then
				rm -f "$f7z"
				errexit "File '$f7z' fails SHA1 check: '$hash_is' should be '$hash'."
			fi
			echo "Extracting '$DBDIR/$f' ..."
			tar xOf "$f7z" | cut -d ":" -f 1 > "$f" || errexit "Decompression of file '$f7z' failed."
			local s_txt_is=$(stat -f %z "$f")
			if [ "$s_txt_is" != "$s_txt" ]; then
				rm -f "$f"
				errexit "File '$f' has size $s_txt_is after decompression, should be $s_txt."
			fi
		fi
		rm -f "$f7z"
	done <<EOF
pwned-passwords-ordered-2.0.txt 20567110522 9647404191 87437926c6293d034a259a2b86a2d077e7fd5a63
EOF
	echo "All data files have been successfully downloaded and extracted."
	# delete old data files (their content is included in the new datafiles)
	while read f
	do
	      rm -f $f $f.7z
	done <<EOF
pwned-passwords-1.0.txt
pwned-passwords-update-1.txt
pwned-passwords-update-2.txt
EOF
}

# Password lookup
exitcode=0

lookup ()
{
    local hash=$(echo "$1" | tr 'a-z' 'A-Z')
    if [ "$USEFILES" = yes ]; then
	look "$hash" pwned-passwords*.txt > /dev/null
    else
	expected=${hash#?????}
	prefix=${hash%$expected}
	fetch -q -o - https://api.pwnedpasswords.com/range/$prefix 2>/dev/null | grep -i "^$expected:" >/dev/null
    fi
}

checkpw ()
{
    local pwd="$1"
    local hash=$(echo -n "$pwd" | sha1)
    if lookup "$hash"; then
	echo "$pwd"
	exitcode=1
    elif expr "$pwd" : '[A-Fa-f0-9]\{40\}$' > /dev/null; then
	if lookup "$pwd"; then
	    echo "$pwd"
	    exitcode=1
	fi
    fi
}

# Main program
export LC_COLLATE=C
if cd "$DBDIR" && ls pwned-passwords*.txt; then
	USEFILES=yes
fi >/dev/null 2>&1

if [ "$#" -gt 0 ]; then
    if [ "$1" = "-u" ]; then
	fetchpwfiles
	exit 0
    else	
	echo "usage: "$(progname)" [-u]"
	exit 2
    fi
fi

while read pwd
do
    checkpw "$pwd"
done

exit $exitcode