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.
---

THIS IS A DRAFT

After much demand for a "team pass" that can live within git, and
instead of relying on gpg.conf for this, I've started to implement
a proper team pass.

Please tell me what you think of this. I want to get it right. Does
it satisfy the demands folks have had? Is it implemented as cleanly
as possible? Feedback wanted.


 man/pass.1            | 18 ++++++----
 src/password-store.sh | 95 +++++++++++++++++++++++++++++++++------------------
 2 files changed, 74 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..3ee23cc 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,38 @@ 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 true; do
+               [[ "$(realpath -q "$current")" == "$(realpath -q "$PREFIX")" ]] 
&& break
+               [[ -f "$current/.gpg-id" ]] && break
+               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 +169,43 @@ 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
+                       echo "Creating new sub-folder $id_path"
+               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 +217,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 +280,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 +293,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 +301,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 +313,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 +328,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 +360,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

_______________________________________________
Password-Store mailing list
[email protected]
http://lists.zx2c4.com/mailman/listinfo/password-store

Reply via email to