[pass] [PATCH v2] Team pass: enable multiple keys and per directory
Jason A. Donenfeld
Jason at zx2c4.com
Thu Mar 20 00:04:43 CET 2014
The .gpg-id file may now have multiple keys in it, one per line.
If a .gpg-id file exists inside a subdirectory, passwords inside that
directory are encrypted to that/those ids.
The init command has learned a -p/--path option for writing such a sub
directory .gpg-id and now can take several arguments for ids.
---
Version 2 no longer relies on realpath and makes other small improvements.
PLEASE SOMEBODY READ THIS WHOLE PATCH!
I'd like some eyeballs to make sure it's perfect.
man/pass.1 | 18 ++++++----
src/password-store.sh | 92 +++++++++++++++++++++++++++++++++------------------
2 files changed, 71 insertions(+), 39 deletions(-)
diff --git a/man/pass.1 b/man/pass.1
index efb5d9b..9399dba 100644
--- a/man/pass.1
+++ b/man/pass.1
@@ -51,15 +51,19 @@ password names in
.SH COMMANDS
.TP
-\fBinit\fP [ \fI--reencrypt\fP, \fI-e\fP ] \fIgpg-id\fP
+\fBinit\fP [ \fI--reencrypt\fP, \fI-e\fP ] [ \fI--path=sub-folder\fP, \fI-p sub-folder\fP ] \fIgpg-id...\fP
Initialize new password storage and use
.I gpg-id
-for encryption. This command must be run first before a password store can be
-used. If \fI--reencrypt\fP or \fI-e\fP is specified, reencrypt all existing
-passwords in the password store using \fIgpg-id\fP. Note that use of
+for encryption. Multiple gpg-ids may be specified, in order to encrypt each
+password with multiple ids. This command must be run first before a password
+store can be used. If \fI--reencrypt\fP or \fI-e\fP is specified, reencrypt
+all existing passwords in the password store using \fIgpg-id\fP. Note that
+use of
.BR gpg-agent (1)
is recommended so that the batch decryption does not require as much user
-intervention.
+intervention. If \fI--path\fP or \fI-p\fP is specified, along with an argument,
+a specific gpg-id or set of gpg-ids is assigned for that specific sub folder of
+the password store.
.TP
\fBls\fP \fIsubfolder\fP
List names of passwords inside the tree at
@@ -322,7 +326,9 @@ The default password storage directory.
.TP
.B ~/.password-store/.gpg-id
Contains the default gpg key identification used for encryption and decryption.
-This should be set using the \fBinit\fP command.
+Multiple gpg keys may be specified in this file, one per line. If this file
+exists in any sub directories, passwords inside those sub directories are
+encrypted using those keys. This should be set using the \fBinit\fP command.
.SH ENVIRONMENT VARIABLES
diff --git a/src/password-store.sh b/src/password-store.sh
index b2e2d0c..7a71ee9 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -6,7 +6,6 @@
umask 077
PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
-ID="$PREFIX/.gpg-id"
GIT_DIR="${PASSWORD_STORE_GIT:-$PREFIX}/.git"
GPG_OPTS="--quiet --yes --batch --compress-algo=none"
@@ -77,6 +76,36 @@ yesno() {
read -p "$1 [y/N] " response
[[ $response == [yY] ]] || exit 1
}
+set_gpg_recipients() {
+ gpg_recipient_args=( )
+
+ if [[ -n $PASSWORD_STORE_KEY ]]; then
+ for gpg_id in $PASSWORD_STORE_KEY; do
+ gpg_recipient_args+=( "-r" "$gpg_id" )
+ done
+ return
+ fi
+
+ local current="$PREFIX/$1"
+ while [[ $current != "$PREFIX" && ! -f $current/.gpg-id ]]; do
+ current="${current%/*}"
+ done
+ current="$current/.gpg-id"
+
+ if [[ ! -f $current ]]; then
+ echo "You must run:"
+ echo " $program init your-gpg-id"
+ echo "before you may use the password store."
+ echo
+ usage
+ exit 1
+ fi
+
+ while read gpg_id; do
+ gpg_recipient_args+=( "-r" "$gpg_id" )
+ done < "$current"
+}
+
#
# BEGIN Platform definable
#
@@ -138,32 +167,42 @@ fi
case "$command" in
init)
reencrypt=0
+ id_path=""
- opts="$($GETOPT -o e -l reencrypt -n "$program" -- "$@")"
+ opts="$($GETOPT -o ep: -l reencrypt,path: -n "$program" -- "$@")"
err=$?
eval set -- "$opts"
while true; do case $1 in
-e|--reencrypt) reencrypt=1; shift ;;
+ -p|--path) id_path="$2"; shift; shift ;;
--) shift; break ;;
esac done
- if [[ $# -ne 1 ]]; then
- echo "Usage: $program $command [--reencrypt,-e] gpg-id"
+ if [[ $# -lt 1 ]]; then
+ echo "Usage: $program $command [--reencrypt,-e] [--path=subfolder,-p subfolder] gpg-id..."
exit 1
fi
+ if [[ -n $id_path && ! -d $PREFIX/$id_path ]]; then
+ if [[ -e $PREFIX/$id_path ]]; then
+ echo "Error: $PREFIX/$id_path exists but is not a directory."
+ exit 1;
+ fi
+ fi
- gpg_id="$1"
- mkdir -v -p "$PREFIX"
- echo "$gpg_id" > "$ID"
- echo "Password store initialized for $gpg_id."
- git_add_file "$ID" "Set GPG id to $gpg_id."
+ mkdir -v -p "$PREFIX/$id_path"
+ gpg_id="$PREFIX/$id_path/.gpg-id"
+ printf "%s\n" "$@" > "$gpg_id"
+ id_print="$(printf "%s, " "$@" | head -c -2)"
+ echo "Password store initialized for $id_print"
+ git_add_file "$gpg_id" "Set GPG id to ${id_print}."
if [[ $reencrypt -eq 1 ]]; then
- find "$PREFIX/" -iname '*.gpg' | while read passfile; do
- gpg2 -d $GPG_OPTS "$passfile" | gpg2 -e -r "$gpg_id" -o "$passfile.new" $GPG_OPTS &&
+ find "$PREFIX/$id_path" -iname '*.gpg' | while read passfile; do
+ set_gpg_recipients "$(sed "s:^$PREFIX/::" <<<"$(dirname "$passfile")")"
+ gpg2 -d $GPG_OPTS "$passfile" | gpg2 -e "${gpg_recipient_args[@]}" -o "$passfile.new" $GPG_OPTS &&
mv -v "$passfile.new" "$passfile"
done
- git_add_file "$PREFIX" "Reencrypted entire store using new GPG id $gpg_id."
+ git_add_file "$PREFIX/$id_path" "Reencrypted password store using new GPG id ${id_print}."
fi
exit 0
;;
@@ -175,22 +214,6 @@ case "$command" in
version
exit 0
;;
-esac
-
-if [[ -n $PASSWORD_STORE_KEY ]]; then
- ID="$PASSWORD_STORE_KEY"
-elif [[ ! -f $ID ]]; then
- echo "You must run:"
- echo " $program init your-gpg-id"
- echo "before you may use the password store."
- echo
- usage
- exit 1
-else
- ID="$(head -n 1 "$ID")"
-fi
-
-case "$command" in
show|ls|list)
clip=0
@@ -254,11 +277,12 @@ case "$command" in
[[ $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?"
mkdir -p -v "$PREFIX/$(dirname "$path")"
+ set_gpg_recipients "$(dirname "$path")"
if [[ $multiline -eq 1 ]]; then
echo "Enter contents of $path and press Ctrl+D when finished:"
echo
- gpg2 -e -r "$ID" -o "$passfile" $GPG_OPTS
+ gpg2 -e "${gpg_recipient_args[@]}" -o "$passfile" $GPG_OPTS
elif [[ $noecho -eq 1 ]]; then
while true; do
read -r -p "Enter password for $path: " -s password
@@ -266,7 +290,7 @@ case "$command" in
read -r -p "Retype password for $path: " -s password_again
echo
if [[ $password == "$password_again" ]]; then
- gpg2 -e -r "$ID" -o "$passfile" $GPG_OPTS <<<"$password"
+ gpg2 -e "${gpg_recipient_args[@]}" -o "$passfile" $GPG_OPTS <<<"$password"
break
else
echo "Error: the entered passwords do not match."
@@ -274,7 +298,7 @@ case "$command" in
done
else
read -r -p "Enter password for $path: " -e password
- gpg2 -e -r "$ID" -o "$passfile" $GPG_OPTS <<<"$password"
+ gpg2 -e "${gpg_recipient_args[@]}" -o "$passfile" $GPG_OPTS <<<"$password"
fi
git_add_file "$passfile" "Added given password for $path to store."
;;
@@ -286,6 +310,7 @@ case "$command" in
path="$1"
mkdir -p -v "$PREFIX/$(dirname "$path")"
+ set_gpg_recipients "$(dirname "$path")"
passfile="$PREFIX/$path.gpg"
template="$program.XXXXXXXXXXXXX"
@@ -300,7 +325,7 @@ case "$command" in
action="Edited"
fi
${EDITOR:-vi} "$tmp_file"
- while ! gpg2 -e -r "$ID" -o "$passfile" $GPG_OPTS "$tmp_file"; do
+ while ! gpg2 -e "${gpg_recipient_args[@]}" -o "$passfile" $GPG_OPTS "$tmp_file"; do
echo "GPG encryption failed. Retrying."
sleep 1
done
@@ -332,13 +357,14 @@ case "$command" in
exit 1
fi
mkdir -p -v "$PREFIX/$(dirname "$path")"
+ set_gpg_recipients "$(dirname "$path")"
passfile="$PREFIX/$path.gpg"
[[ $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?"
pass="$(pwgen -s $symbols $length 1)"
[[ -n $pass ]] || exit 1
- gpg2 -e -r "$ID" -o "$passfile" $GPG_OPTS <<<"$pass"
+ gpg2 -e "${gpg_recipient_args[@]}" -o "$passfile" $GPG_OPTS <<<"$pass"
git_add_file "$passfile" "Added generated password for $path to store."
if [[ $clip -eq 0 ]]; then
--
1.9.0
More information about the Password-Store
mailing list