diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2014-03-19 02:04:29 -0600 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2014-03-19 18:15:17 -0600 |
commit | b1314982194c99361c2b81b3359a21d5a289fdb5 (patch) | |
tree | eab7f53ce7ccc3c3e3f33344186d121679695867 | |
parent | c31bbd546a9be36f335345a9dad33676de2dbcb6 (diff) |
Team pass: enable multiple keys and per directory
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.
-rw-r--r-- | man/pass.1 | 18 | ||||
-rwxr-xr-x | src/password-store.sh | 97 |
2 files changed, 75 insertions, 40 deletions
@@ -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..9f733f7 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" @@ -74,9 +73,39 @@ git_add_file() { git commit -m "$2" } yesno() { - read -p "$1 [y/N] " response + read -r -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 -r gpg_id; do + gpg_recipient_args+=( "-r" "$gpg_id" ) + done < "$current" +} + # # BEGIN Platform definable # @@ -138,32 +167,45 @@ 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 2 ;; --) shift; break ;; esac done - if [[ $# -ne 1 ]]; then - echo "Usage: $program $command [--reencrypt,-e] gpg-id" + if [[ $err -ne 0 || $# -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, " "$@")" + 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 -r passfile; do + passfile_dir=${passfile%/*} + passfile_dir=${passfile_dir#$PREFIX} + passfile_dir=${passfile_dir#/} + set_gpg_recipients "$passfile_dir" + 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 |