From a6b56a3515c8872c73bd10571f929942e4e96704 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Kj=C3=A4ll?= <alexander.kjall@gmail.com>
Date: Fri, 13 Nov 2020 14:26:59 +0100
Subject: [PATCH 2/2] When decrypting a password file, first verify that there
 is a signature notation with the name filename@passwordstore.org that
 contains the filename of the file the operation is performed on, relative to
 the password store base path

---
 src/password-store.sh | 77 ++++++++++++++++++++++++++++---------------
 1 file changed, 50 insertions(+), 27 deletions(-)

diff --git a/src/password-store.sh b/src/password-store.sh
index 39c60b8..f362780 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -67,6 +67,17 @@ verify_file() {
 	done
 	[[ $found -eq 1 ]] || die "Signature for $1 is invalid."
 }
+verify_password_file() {
+	[[ -n $PASSWORD_STORE_SIGNING_KEY ]] || return 0
+	local status="$($GPG $PASSWORD_STORE_GPG_OPTS --decrypt --status-fd=1 "$1" 2>/dev/null)"
+	local good_sig=0
+	if [[ $status = *"[GNUPG:] NOTATION_NAME filename@passwordstore.org"* ]]; then
+		if [[ $status = *"[GNUPG:] NOTATION_DATA $2"* ]]; then
+			good_sig=1;
+		fi
+	fi
+	[[ $good_sig -eq 1 ]] || die "Filename notation for $2 is invalid."
+}
 set_gpg_recipients() {
 	GPG_RECIPIENT_ARGS=( )
 	GPG_RECIPIENTS=( )
@@ -130,9 +141,11 @@ reencrypt_path() {
 		current_keys="$(LC_ALL=C $GPG $PASSWORD_STORE_GPG_OPTS -v --no-secmem-warning --no-permission-warning --decrypt --list-only --keyid-format long "$passfile" 2>&1 | sed -n 's/^gpg: public key is \([A-F0-9]\+\)$/\1/p' | LC_ALL=C sort -u)"
 
 		if [[ $gpg_keys != "$current_keys" ]]; then
-			echo "$passfile_display: reencrypting to ${gpg_keys//$'\n'/ }"
-			$GPG -d "${GPG_OPTS[@]}" "$passfile" | $GPG -se --sig-notation filename@passwordstore.org="$passfile_display".gpg "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile_temp" "${GPG_OPTS[@]}" &&
-			mv "$passfile_temp" "$passfile" || rm -f "$passfile_temp"
+			if verify_password_file "$passfile" "$passfile_display.gpg" ; then
+				echo "$passfile_display: reencrypting to ${gpg_keys//$'\n'/ }"
+				$GPG -d "${GPG_OPTS[@]}" "$passfile" 2>/dev/null | $GPG -se --sig-notation filename@passwordstore.org="$passfile_display".gpg "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile_temp" "${GPG_OPTS[@]}" &&
+				mv "$passfile_temp" "$passfile" || rm -f "$passfile_temp"
+			fi
 		fi
 		prev_gpg_recipients="${GPG_RECIPIENTS[*]}"
 	done < <(find "$1" -path '*/.git' -prune -o -iname '*.gpg' -print0)
@@ -382,16 +395,20 @@ cmd_show() {
 	check_sneaky_paths "$path"
 	if [[ -f $passfile ]]; then
 		if [[ $clip -eq 0 && $qrcode -eq 0 ]]; then
-			pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | $BASE64)" || exit $?
-			echo "$pass" | $BASE64 -d
+			if verify_password_file "$passfile" "$path.gpg" ; then
+				pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" 2>/dev/null | $BASE64)" || exit $?
+				echo "$pass" | $BASE64 -d
+			fi
 		else
 			[[ $selected_line =~ ^[0-9]+$ ]] || die "Clip location '$selected_line' is not a number."
-			pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | tail -n +${selected_line} | head -n 1)" || exit $?
-			[[ -n $pass ]] || die "There is no password to put on the clipboard at line ${selected_line}."
-			if [[ $clip -eq 1 ]]; then
-				clip "$pass" "$path"
-			elif [[ $qrcode -eq 1 ]]; then
-				qrcode "$pass" "$path"
+			if verify_password_file "$passfile" "$path.gpg" ; then
+				pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" 2>/dev/null | tail -n +${selected_line} | head -n 1)" || exit $?
+				[[ -n $pass ]] || die "There is no password to put on the clipboard at line ${selected_line}."
+				if [[ $clip -eq 1 ]]; then
+					clip "$pass" "$path"
+				elif [[ $qrcode -eq 1 ]]; then
+					qrcode "$pass" "$path"
+				fi
 			fi
 		fi
 	elif [[ -d $PREFIX/$path ]]; then
@@ -419,15 +436,17 @@ cmd_grep() {
 	[[ $# -lt 1 ]] && die "Usage: $PROGRAM $COMMAND [GREPOPTIONS] search-string"
 	local passfile grepresults
 	while read -r -d "" passfile; do
-		grepresults="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | grep --color=always "$@")"
-		[[ $? -ne 0 ]] && continue
-		passfile="${passfile%.gpg}"
-		passfile="${passfile#$PREFIX/}"
-		local passfile_dir="${passfile%/*}/"
-		[[ $passfile_dir == "${passfile}/" ]] && passfile_dir=""
-		passfile="${passfile##*/}"
-		printf "\e[94m%s\e[1m%s\e[0m:\n" "$passfile_dir" "$passfile"
-		echo "$grepresults"
+		if verify_password_file "$passfile" "${passfile#$PREFIX/}" ; then
+			grepresults="$($GPG -d "${GPG_OPTS[@]}" "$passfile" 2>/dev/null | grep --color=always "$@")"
+			[[ $? -ne 0 ]] && continue
+			passfile="${passfile%.gpg}"
+			passfile="${passfile#$PREFIX/}"
+			local passfile_dir="${passfile%/*}/"
+			[[ $passfile_dir == "${passfile}/" ]] && passfile_dir=""
+			passfile="${passfile##*/}"
+			printf "\e[94m%s\e[1m%s\e[0m:\n" "$passfile_dir" "$passfile"
+			echo "$grepresults"
+		fi
 	done < <(find -L "$PREFIX" -path '*/.git' -prune -o -iname '*.gpg' -print0)
 }
 
@@ -495,8 +514,10 @@ cmd_edit() {
 
 	local action="Add"
 	if [[ -f $passfile ]]; then
-		$GPG -d -o "$tmp_file" "${GPG_OPTS[@]}" "$passfile" || exit 1
-		action="Edit"
+		if verify_password_file "$passfile" "$path.gpg" ; then
+			$GPG -d -o "$tmp_file" "${GPG_OPTS[@]}" "$passfile" 2>/dev/null || exit 1
+			action="Edit"
+		fi
 	fi
 	${EDITOR:-vi} "$tmp_file"
 	[[ -f $tmp_file ]] || die "New password not saved."
@@ -540,11 +561,13 @@ cmd_generate() {
 		echo "$pass" | $GPG -se --sig-notation filename@passwordstore.org="$path.gpg" "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" || die "Password encryption aborted."
 	else
 		local passfile_temp="${passfile}.tmp.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}.--"
-		if { echo "$pass"; $GPG -d "${GPG_OPTS[@]}" "$passfile" | tail -n +2; } | $GPG -se --sig-notation filename@passwordstore.org="$path.gpg" "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile_temp" "${GPG_OPTS[@]}"; then
-			mv "$passfile_temp" "$passfile"
-		else
-			rm -f "$passfile_temp"
-			die "Could not reencrypt new password."
+		if verify_password_file "$passfile" "$path.gpg" ; then
+			if { echo "$pass"; $GPG -d "${GPG_OPTS[@]}" "$passfile" 2>/dev/null | tail -n +2; } | $GPG -se --sig-notation filename@passwordstore.org="$path.gpg" "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile_temp" "${GPG_OPTS[@]}"; then
+				mv "$passfile_temp" "$passfile"
+			else
+				rm -f "$passfile_temp"
+				die "Could not reencrypt new password."
+			fi
 		fi
 	fi
 	local verb="Add"
-- 
2.27.0

