commit 8769aa6370cd6b4ebd7393c32519a04772f5bd29
parent 564e87519bf366892bb33b1553f5fa00a57ed66f
Author: Jason A. Donenfeld <Jason@zx2c4.com>
Date: Mon, 3 Sep 2012 04:43:01 +0200
Make into a real project.
Diffstat:
9 files changed, 412 insertions(+), 245 deletions(-)
diff --git a/COPYING b/COPYING
@@ -0,0 +1,16 @@
+Password Store is Copyright (C) 2012 Jason A. Donenfeld <Jason@zx2c4.com>.
+
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
diff --git a/INSTALL b/INSTALL
@@ -0,0 +1,5 @@
+Simply typing
+
+ make install
+
+should install pass to the standard locations.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,10 @@
+all:
+ @echo "Password store is a shell script, so there is nothing to do. Try \"make install\" instead."
+
+install:
+ @install -v src/password-store.sh /usr/bin/pass
+ @install -v man/pass.1 /usr/share/man/man1/pass.1
+ @install -v bash-completion/pass-bash-completion.sh /usr/share/bash-completion/pass
+
+uninstall:
+ @rm -vf /usr/bin/pass /usr/share/man/man1/pass.1 /usr/share/bash-completion/pass
diff --git a/README b/README
@@ -25,7 +25,7 @@ Usage:
pass generate [--no-symbols,-n] [--clip,-c] pass-name pass-length
Generate a new password of pass-length with optionally no symbols.
Optionally put it on the clipboard and clear board after 45 seconds.
- pass remove pass-name
+ pass rm pass-name
Remove existing password.
pass push
If the password store is a git repository, push the latest changes.
@@ -37,6 +37,9 @@ Usage:
pass help
Show this text.
+See the man page for more options -- man 1 pass.
+
+
Examples:
- Initialize password store:
diff --git a/bash-completion.sh b/bash-completion/pass-bash-completion.sh
diff --git a/man/pass.1 b/man/pass.1
@@ -0,0 +1,135 @@
+.TH PASS 1 "2012 Sept 2" ZX2C4 "Password Store"
+
+.SH NAME
+pass - stores, retrieves, generates, and synchronizes passwords securely
+
+.SH SYNOPSIS
+.B pass
+[
+.I COMMAND
+] [
+.I OPTIONS
+]... [
+.I ARGS
+]...
+
+.SH DESCRIPTION
+
+.B pass
+is a very simple password store that keeps passwords inside
+.BR gpg (1)
+encrypted files inside a simple directory tree residing at
+.IR $HOME/.password-store .
+The
+.B pass
+utility provides a series of commands for manipulating the password store,
+allowing the user to add, remove, edit, synchronize, generate, and manipulate
+passwords.
+
+If no COMMAND is specified, COMMAND defaults to either
+.B show
+or
+.BR ls ,
+depending on the type of specifier in ARGS. Otherwise COMMAND must be one of
+the valid commands listed below.
+
+Several of the commands below rely on or provide additional functionality if
+the password store directory is also a git repository. If the password store
+directory is a git repository, all password store modification commands will
+cause a corresponding git commit.
+
+The \fBinit\fP command must be run before other commands in order to initialize
+the password store with the correct gpg key id.
+
+.SH COMMANDS
+
+.TP
+\fBinit\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.
+.TP
+\fBls\fP \fIsubfolder\fP
+List names of passwords inside the tree at
+.I subfolder
+by using the
+.BR tree (1)
+program. This command is alternatively named \fBlist\fP.
+.TP
+\fBshow\fP [ \fI--clip\fP, \fI-c\fP ] \fIpass-name\fP
+Decrypt and print a password named \fIpass-name\fP. If \fI--clip\fP or \fI-c\fP
+is specified, do not print the password but instead copy it to the clipboard
+using
+.BR xclip (1)
+and then restore the clipboard after 45 seconds.
+.TP
+\fBinsert\fP [ \fI--multiline\fP, \fI-m\fP ] \fIpass-name\fP
+Insert a new password into the password store called \fIpass-name\fP. This will
+read the new password from standard in. If \fI--multiline\fP or \fI-m\fP is
+specified, lines will be read until EOF or Ctrl+D is reached. Otherwise, only
+a single line from standard in is read.
+.TP
+\fBgenerate\fP [ \fI--no-symbols\fP, \fI-n\fP ] [ \fI--clip\fP, \fI-c\fP ] \fIpass-name pass-length\fP
+Generate a new password using
+.BR pwgen (1)
+of length \fIpass-length\fP and insert into \fIpass-name\fP. If \fI--no-symbols\fP or \fI-n\fP
+is specified, do not use any non-alphanumeric characters in the generated password.
+If \fI--clip\fP or \fI-c\fP is specified, do not print the password but instead copy
+it to the clipboard using
+.BR xclip (1)
+and then restore the clipboard after 45 seconds.
+.TP
+\fBrm\fP \fIpass-name\fP
+Remove the password named \fIpass-name\fP from the password store. This command is
+alternatively named \fBremove\fP.
+.TP
+\fBpush\fP
+If the password store is a git repository, push the latest changes using
+.BR git-push (1).
+.TP
+\fBpull\fP
+If the password store is a git repository, pull the latest changes using
+.BR git-pull (1).
+.TP
+\fBgit\fP \fIgit-command-args\fP...
+If the password store is a git repository, pass \fIgit-command-args\fP as arguments to
+.BR git (1)
+using the password store as the git repository.
+.TP
+\fBhelp\fP
+Show usage message.
+
+.SH FILES
+
+.TP
+\fB~/.password-store\fP
+The password storage directory.
+.TP
+\fB~/.password-store/.gpg-id\fP
+Contains the gpg key identification used for encryption and decryption. This should
+be set using the \fBinit\fP command.
+
+.SH SEE ALSO
+.BR gpg (1),
+.BR pwgen (1),
+.BR git (1),
+.BR xclip (1).
+
+.SH AUTHOR
+Jason A. Donenfeld <Jason@zx2c4.com>
+
+.SH COPYING
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/pass b/pass
@@ -1 +0,0 @@
-password-store.sh
-\ No newline at end of file
diff --git a/password-store.sh b/password-store.sh
@@ -1,242 +0,0 @@
-#!/bin/bash
-
-umask 077
-
-PREFIX="$HOME/.password-store"
-ID="$PREFIX/.gpg-id"
-GIT="$PREFIX/.git"
-
-export GIT_DIR="$GIT"
-export GIT_WORK_TREE="$PREFIX"
-
-usage() {
- cat <<_EOF
-Password Store
-by Jason Donenfeld
- Jason@zx2c4.com
-
-Usage:
- $program init gpg-id
- Initialize new password storage and use gpg-id for encryption.
- $program [ls] [subfolder]
- List passwords.
- $program [show] [--clip,-c] pass-name
- Show existing password and optionally put it on the clipboard.
- If put on the clipboard, it will be cleared in 45 seconds.
- $program insert [--multiline,-m] pass-name
- Insert new optionally multiline password.
- $program generate [--no-symbols,-n] [--clip,-c] pass-name pass-length
- Generate a new password of pass-length with optionally no symbols.
- Optionally put it on the clipboard and clear board after 45 seconds.
- $program remove pass-name
- Remove existing password.
- $program push
- If the password store is a git repository, push the latest changes.
- $program pull
- If the password store is a git repository, pull the latest changes.
- $program git git-command-args...
- If the password store is a git repository, execute a git command
- specified by git-command-args.
- $program help
- Show this text.
-_EOF
-}
-isCommand() {
- case "$1" in
- init|ls|show|insert|generate|remove|rm|delete|push|pull|git|help) return 0 ;;
- *) return 1 ;;
- esac
-}
-clip() {
- # This base64 business is a disgusting hack to deal with newline inconsistancies
- # in shell. There must be a better way to deal with this, but because I'm a dolt,
- # we're going with this for now.
-
- before="$(xclip -o -selection clipboard | base64)"
- echo -n "$1" | xclip -selection clipboard
- (
- sleep 45s
- now="$(xclip -o -selection clipboard | base64)"
- if [[ $now != $(echo -n "$1" | base64) ]]; then
- before="$now"
- fi
- # It might be nice to programatically check to see if klipper exists,
- # as well as checking for other common clipboard managers. But for now,
- # this works fine. Clipboard managers frequently write their history
- # out in plaintext, so we axe it here.
- qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory >/dev/null 2>&1
- echo "$before" | base64 -d | xclip -selection clipboard
- ) & disown
- echo "Copied $2 to clipboard. Will clear in 45 seconds."
-}
-program="$(basename "$0")"
-command="$1"
-if isCommand "$command"; then
- shift
-else
- command="show"
-fi
-
-case "$command" in
- init)
- if [[ $# -ne 1 ]]; then
- echo "Usage: $program $command gpg-id"
- exit 1
- fi
- gpg_id="$1"
- mkdir -v -p "$PREFIX"
- echo "$gpg_id" > "$ID"
- echo "Password store initialized for $gpg_id."
- exit 0
- ;;
- help)
- usage
- exit 0
- ;;
-esac
-
-if ! [[ -f $ID ]]; then
- echo "You must run:"
- echo " $0 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)
- clip=0
- if [[ $1 == "--clip" || $1 == "-c" ]]; then
- clip=1
- shift
- fi
- path="$1"
- if [[ -d $PREFIX/$path ]]; then
- if [[ $path == "" ]]; then
- echo "Password Store"
- else
- echo $path
- fi
- tree "$PREFIX/$path" | tail -n +2 | head -n -2 | sed 's/\(.*\)\.gpg$/\1/';
- else
- passfile="$PREFIX/$path.gpg"
- if ! [[ -f $passfile ]]; then
- echo "$path is not in the password store."
- exit 1
- fi
- if [ $clip -eq 0 ]; then
- exec gpg -q -d "$passfile"
- else
- clip $(gpg -q -d "$passfile") $path
- fi
- fi
- ;;
- insert)
- ml=0
- if [[ $1 == "--multiline" || $1 == "-m" ]]; then
- ml=1
- shift
- fi
- if [[ $# -ne 1 ]]; then
- echo "Usage: $program $command [--multiline,-m] pass-name"
- exit 1
- fi
- path="$1"
- mkdir -p -v "$PREFIX/$(dirname "$path")"
-
- passfile="$PREFIX/$path.gpg"
- if [[ $ml -eq 0 ]]; then
- echo -n "Enter password for $path: "
- head -n 1 | gpg -e -r "$ID" > "$passfile"
- else
- echo "Enter contents of $path and press Ctrl+D when finished:"
- echo
- cat | gpg -e -r "$ID" > "$passfile"
- fi
- if [[ -d $GIT ]]; then
- git add "$passfile"
- git commit -m "Added given password for $path to store."
- fi
- ;;
- generate)
- clip=0
- symbols="-y"
- while true; do
- if [[ $1 == "--no-symbols" || $1 == "-n" ]]; then
- symbols=""
- shift
- elif [[ $1 == "--clip" || $1 == "-c" ]]; then
- clip=1
- shift
- else
- break
- fi
- done
- if [[ $# -ne 2 ]]; then
- echo "Usage: $program $command [--no-symbols,-n] [--clip,-c] pass-name pass-length"
- exit 1
- fi
- path="$1"
- length="$2"
- if ! [[ $length =~ ^[0-9]+$ ]]; then
- echo "pass-length \"$length\" must be a number."
- exit 1
- fi
- mkdir -p -v "$PREFIX/$(dirname "$path")"
- pass="$(pwgen -s $symbols $length 1)"
- passfile="$PREFIX/$path.gpg"
- echo $pass | gpg -e -r "$ID" > "$passfile"
- if [[ -d $GIT ]]; then
- git add "$passfile"
- git commit -m "Added generated password for $path to store."
- fi
-
- if [ $clip -eq 0 ]; then
- echo "The generated password to $path is:"
- echo "$pass"
- else
- clip "$pass" "$path"
- fi
- ;;
- delete|rm|remove)
- if [[ $# -ne 1 ]]; then
- echo "Usage: $program $command pass-name"
- exit
- fi
- path="$1"
- passfile="$PREFIX/$path.gpg"
- if ! [[ -f $passfile ]]; then
- echo "$path is not in the password store."
- exit 1
- fi
- rm -i -v "$passfile"
- if [[ -d $GIT ]] && ! [[ -f $passfile ]]; then
- git rm -f "$passfile"
- git commit -m "Removed $path from store."
- fi
- ;;
- push|pull)
- if [[ -d $GIT ]]; then
- exec git $command $@
- else
- echo "Error: the password store is not a git repository."
- exit 1
- fi
- ;;
- git)
- if [[ -d $GIT ]]; then
- exec git $@
- else
- echo "Error: the password store is not a git repository."
- exit 1
- fi
- ;;
- *)
- usage
- exit 1
- ;;
-esac
-exit 0
diff --git a/src/password-store.sh b/src/password-store.sh
@@ -0,0 +1,242 @@
+#!/bin/bash
+
+umask 077
+
+PREFIX="$HOME/.password-store"
+ID="$PREFIX/.gpg-id"
+GIT="$PREFIX/.git"
+
+export GIT_DIR="$GIT"
+export GIT_WORK_TREE="$PREFIX"
+
+usage() {
+ cat <<_EOF
+Password Store
+by Jason Donenfeld
+ Jason@zx2c4.com
+
+Usage:
+ $program init gpg-id
+ Initialize new password storage and use gpg-id for encryption.
+ $program [ls] [subfolder]
+ List passwords.
+ $program [show] [--clip,-c] pass-name
+ Show existing password and optionally put it on the clipboard.
+ If put on the clipboard, it will be cleared in 45 seconds.
+ $program insert [--multiline,-m] pass-name
+ Insert new optionally multiline password.
+ $program generate [--no-symbols,-n] [--clip,-c] pass-name pass-length
+ Generate a new password of pass-length with optionally no symbols.
+ Optionally put it on the clipboard and clear board after 45 seconds.
+ $program rm pass-name
+ Remove existing password.
+ $program push
+ If the password store is a git repository, push the latest changes.
+ $program pull
+ If the password store is a git repository, pull the latest changes.
+ $program git git-command-args...
+ If the password store is a git repository, execute a git command
+ specified by git-command-args.
+ $program help
+ Show this text.
+_EOF
+}
+isCommand() {
+ case "$1" in
+ init|ls|list|show|insert|generate|remove|rm|delete|push|pull|git|help) return 0 ;;
+ *) return 1 ;;
+ esac
+}
+clip() {
+ # This base64 business is a disgusting hack to deal with newline inconsistancies
+ # in shell. There must be a better way to deal with this, but because I'm a dolt,
+ # we're going with this for now.
+
+ before="$(xclip -o -selection clipboard | base64)"
+ echo -n "$1" | xclip -selection clipboard
+ (
+ sleep 45s
+ now="$(xclip -o -selection clipboard | base64)"
+ if [[ $now != $(echo -n "$1" | base64) ]]; then
+ before="$now"
+ fi
+ # It might be nice to programatically check to see if klipper exists,
+ # as well as checking for other common clipboard managers. But for now,
+ # this works fine. Clipboard managers frequently write their history
+ # out in plaintext, so we axe it here.
+ qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory >/dev/null 2>&1
+ echo "$before" | base64 -d | xclip -selection clipboard
+ ) & disown
+ echo "Copied $2 to clipboard. Will clear in 45 seconds."
+}
+program="$(basename "$0")"
+command="$1"
+if isCommand "$command"; then
+ shift
+else
+ command="show"
+fi
+
+case "$command" in
+ init)
+ if [[ $# -ne 1 ]]; then
+ echo "Usage: $program $command gpg-id"
+ exit 1
+ fi
+ gpg_id="$1"
+ mkdir -v -p "$PREFIX"
+ echo "$gpg_id" > "$ID"
+ echo "Password store initialized for $gpg_id."
+ exit 0
+ ;;
+ help)
+ usage
+ exit 0
+ ;;
+esac
+
+if ! [[ -f $ID ]]; then
+ echo "You must run:"
+ echo " $0 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
+ if [[ $1 == "--clip" || $1 == "-c" ]]; then
+ clip=1
+ shift
+ fi
+ path="$1"
+ if [[ -d $PREFIX/$path ]]; then
+ if [[ $path == "" ]]; then
+ echo "Password Store"
+ else
+ echo $path
+ fi
+ tree "$PREFIX/$path" | tail -n +2 | head -n -2 | sed 's/\(.*\)\.gpg$/\1/';
+ else
+ passfile="$PREFIX/$path.gpg"
+ if ! [[ -f $passfile ]]; then
+ echo "$path is not in the password store."
+ exit 1
+ fi
+ if [ $clip -eq 0 ]; then
+ exec gpg -q -d "$passfile"
+ else
+ clip $(gpg -q -d "$passfile") $path
+ fi
+ fi
+ ;;
+ insert)
+ ml=0
+ if [[ $1 == "--multiline" || $1 == "-m" ]]; then
+ ml=1
+ shift
+ fi
+ if [[ $# -ne 1 ]]; then
+ echo "Usage: $program $command [--multiline,-m] pass-name"
+ exit 1
+ fi
+ path="$1"
+ mkdir -p -v "$PREFIX/$(dirname "$path")"
+
+ passfile="$PREFIX/$path.gpg"
+ if [[ $ml -eq 0 ]]; then
+ echo -n "Enter password for $path: "
+ head -n 1 | gpg -e -r "$ID" > "$passfile"
+ else
+ echo "Enter contents of $path and press Ctrl+D when finished:"
+ echo
+ cat | gpg -e -r "$ID" > "$passfile"
+ fi
+ if [[ -d $GIT ]]; then
+ git add "$passfile"
+ git commit -m "Added given password for $path to store."
+ fi
+ ;;
+ generate)
+ clip=0
+ symbols="-y"
+ while true; do
+ if [[ $1 == "--no-symbols" || $1 == "-n" ]]; then
+ symbols=""
+ shift
+ elif [[ $1 == "--clip" || $1 == "-c" ]]; then
+ clip=1
+ shift
+ else
+ break
+ fi
+ done
+ if [[ $# -ne 2 ]]; then
+ echo "Usage: $program $command [--no-symbols,-n] [--clip,-c] pass-name pass-length"
+ exit 1
+ fi
+ path="$1"
+ length="$2"
+ if ! [[ $length =~ ^[0-9]+$ ]]; then
+ echo "pass-length \"$length\" must be a number."
+ exit 1
+ fi
+ mkdir -p -v "$PREFIX/$(dirname "$path")"
+ pass="$(pwgen -s $symbols $length 1)"
+ passfile="$PREFIX/$path.gpg"
+ echo $pass | gpg -e -r "$ID" > "$passfile"
+ if [[ -d $GIT ]]; then
+ git add "$passfile"
+ git commit -m "Added generated password for $path to store."
+ fi
+
+ if [ $clip -eq 0 ]; then
+ echo "The generated password to $path is:"
+ echo "$pass"
+ else
+ clip "$pass" "$path"
+ fi
+ ;;
+ delete|rm|remove)
+ if [[ $# -ne 1 ]]; then
+ echo "Usage: $program $command pass-name"
+ exit
+ fi
+ path="$1"
+ passfile="$PREFIX/$path.gpg"
+ if ! [[ -f $passfile ]]; then
+ echo "$path is not in the password store."
+ exit 1
+ fi
+ rm -i -v "$passfile"
+ if [[ -d $GIT ]] && ! [[ -f $passfile ]]; then
+ git rm -f "$passfile"
+ git commit -m "Removed $path from store."
+ fi
+ ;;
+ push|pull)
+ if [[ -d $GIT ]]; then
+ exec git $command $@
+ else
+ echo "Error: the password store is not a git repository."
+ exit 1
+ fi
+ ;;
+ git)
+ if [[ -d $GIT ]]; then
+ exec git $@
+ else
+ echo "Error: the password store is not a git repository."
+ exit 1
+ fi
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+esac
+exit 0