Skip to content

Instantly share code, notes, and snippets.

@Boldewyn
Last active August 29, 2015 14:15
Show Gist options
  • Save Boldewyn/20f3939b2e1ba47c60e1 to your computer and use it in GitHub Desktop.
Save Boldewyn/20f3939b2e1ba47c60e1 to your computer and use it in GitHub Desktop.
pw: a convenient password retriever
#!/bin/bash
#
# Convenient password retrieval
#
# Get your immemorable password by its alias name.
#
# Requirements:
# * either xclip or xsel (Linux), pbclip (OSX), Cygwin's putclip (Win)
# * standard tools: sed, column, tr
# * gpg or gpg2
# * the 'moreutils' package, if you want to edit passwords easily
#
# the gpg-encrypted password file in the to-be-expected-by-XDG place
# (see http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html)
PASSWORD_FILE="${XDG_DATA_HOME:-$HOME/.local/share}/pw.gpg"
# change this, if you use GNU screen and fiddled with the bufferfile option
SCREEN_EXCHANGE=/tmp/screen-exchange
# The GPG binary to use
GPG=gpg
if hash gpg2 2>/dev/null; then
GPG=gpg2
fi
# Allowed characters for passwords, when generated. Default: as much of ASCII
# as feasible
ALLOWED_PASSWORD_CHARS='!-Z_a-~'
# length of generated password
DEFAULT_PASSWORD_LENGTH=32
# Load the preference file, if it exists. Search in the standardized place
# first
if [[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/pw ]]; then
. ${XDG_CONFIG_HOME:-$HOME/.config}/pw
elif [[ -f ~/.pwrc ]]; then
. ~/.pwrc
fi
# first CLI parameter
NAME=$1
#### show HELP and exit
if [[ $NAME == "-h" || $NAME == "--help" ]]; then
echo "usage: $(basename $0) [NAME]"
echo ""
echo "Store and retrieve passwords securely"
echo ""
echo "Options"
echo ""
echo " -l, --list list available password names"
echo " -e, --edit edit the password file"
echo " -g, --generate NAME generate a new password with"
echo " the associated name NAME"
echo ""
echo "Description"
echo ""
echo " Return the password associated with NAME. If NAME"
echo " is -l or --list, list available NAMEs. If NAME is"
echo " omitted, show a list of all NAMEs to select from."
echo ""
echo " Specify -e or --edit to edit the password storage"
echo " file. (Requires the moreutils package.)"
echo ""
echo " With -g or --generate generate a password automa-"
echo " tically."
exit
fi
#### EDIT the password file and exit
if [[ $NAME == "-e" || $NAME == "--edit" ]]; then
if [[ ! $PASSWORD_FILE || ! -f $PASSWORD_FILE ]]; then
echo "No password file found." >&2
if [[ $PASSWORD_FILE ]]; then
echo "To initialize one, run:" >&2
echo "\$ touch /tmp/pw" >&2
echo "\$ $GPG --default-recipient-self --output $PASSWORD_FILE --encrypt /tmp/pw" >&2
fi
exit 1
fi
if ! hash vipe 2>/dev/null; then
echo "vipe not found. Please install the 'moreutils' package." >&2
exit 2
fi
$GPG --quiet --decrypt "$PASSWORD_FILE" | \
vipe | \
$GPG --batch --yes --default-recipient-self --output "$PASSWORD_FILE" --encrypt -
exit
fi
#### GENERATE a password and exit
if [[ $NAME == "-g" || $NAME == "--generate" ]]; then
CONTENT=
if [[ $PASSWORD_FILE && -f $PASSWORD_FILE ]]; then
CONTENT=$($GPG --quiet --decrypt "$PASSWORD_FILE")
fi
REALNAME=$2
if [[ ! $REALNAME ]]; then
echo "Enter a name as mnemonic for this password:" >&2
read REALNAME
fi
echo "$CONTENT" |
cat - <( printf "%s:%s # generated on %s\n" \
"$REALNAME" \
"$( cat /dev/urandom | tr -d -c "$ALLOWED_PASSWORD_CHARS" | head -c "$DEFAULT_PASSWORD_LENGTH" )" \
"$(date +%c)" ) |
$GPG --batch --yes --default-recipient-self --output "$PASSWORD_FILE" --encrypt -
exit
fi
# this is the password storage. Syntax:
# NAME:PASSWORD( # COMMENT)?
# The space before the comment hash is mandatory.
# If you want to have a quick and dirty solution, enter the data below
# into the EOL-marked heredoc.
# You can make this a bit safer (no plain-text passwords) by writing
# the content in a special file $PASSWORD_FILE and encrypting it with gpg2.
# Of course this goes at the cost of easily adding new passwords.
if [[ $PASSWORD_FILE && -f $PASSWORD_FILE ]]; then
LIST="$($GPG --quiet --decrypt "$PASSWORD_FILE" | sed -e '/^$/d' -e '/^#/d')"
else
read -r -d '' LIST <<EOL
name:password # optional comment
EOL
fi
# copy stdin to every imaginable (and available) clipboard
function _pw_copy {
local _pwd=$(cat -)
if [[ $_pwd ]]; then
case "$OSTYPE" in
darwin*)
# set the OSX pasteboard
if hash pbclip 2>/dev/null; then
echo -n "$_pwd" | pbclip
else
echo 'pbclip not found.' >&2
fi
;;
linux*)
# set both X11 clipboards
if hash xclip 2>/dev/null; then
echo -n "$_pwd" | xclip -selection primary
echo -n "$_pwd" | xclip -selection clipboard
elif hash xsel 2>/dev/null; then
echo -n "$_pwd" | xsel
echo -n "$_pwd" | xsel --clipboard
else
echo 'neither xclip nor xsel found.' >&2
fi
;;
cygwin*)
# set the Cygwin/Windows clipboard
if hash putclip 2>/dev/null; then
echo -n "$_pwd" | putclip
else
echo 'putclip not found.' >&2
fi
;;
*)
echo "no clipboard handler found for $OSTYPE." >&2
# do not exit, since we still might be able to set
# screen clipboard below.
;;
esac
if [[ $SCREEN_EXCHANGE && $STY ]]; then
# we are in a screen session: set screen clipboard, too
echo -n "$_pwd" > "$SCREEN_EXCHANGE"
screen -X readbuf
rm -f "$SCREEN_EXCHANGE"
fi
else
echo "No password found for $NAME." >&2
fi
}
if [[ $NAME == "-l" || $NAME == "--list" ]]; then
echo "$LIST" | \
sed "s/:\\([^ ]#\\|[^#]\\)*//" | \
sed 's/#/\t/' | \
column -t -s $'\t'
elif [[ ! $NAME ]]; then
echo "Choose password to copy:" >&2
echo "$LIST" | \
sed "s/:\\([^ ]#\\|[^#]\\)*//" | \
sed '/^#/d' | \
sed '/^$/d' | \
sed 's/#/\t/' | \
cat -n | \
column -t -s $'\t' >&2
echo "Select a number:" >&2
read q
if [[ $q ]]; then
i=0
IFS=$'\n'
for LINE in $LIST; do
if [[ ! $(echo $LINE | tr -d '[[:space:]]') ]]; then
continue
fi
case $LINE in
[^#]*)
let 'i+=1'
;;
esac
if [[ $i == $q ]]; then
NAME="entry $q"
echo "$LINE" | \
sed 's/^[^:]\+://' | \
sed 's/ \+#.*$//' | \
tr -d '\n' | \
_pw_copy
break
fi
done
if [[ ! $NAME ]]; then
echo "Invalid number specified." >&2
exit 2
fi
fi
else
echo "$LIST" | \
sed -n "/^$NAME:/s/^$NAME://p" | \
sed 's/ \+#.*$//' | \
tr -d '\n' | \
_pw_copy
fi
# vim: ft=sh shiftwidth=4 softtabstop=4 tabstop=4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment