Password Manager
Jul 21 2022 · LastMod: Jul 21 2022
A password manager written in bash
(not POSIX shell
but bash
). Requires fzf
or demnu
and such for fuzzy-finding the STDIN
inputs and write the selected items tothe STDOUT
. password
encrypts and stores passwords via pgp
in a single file (PASSWORD_STORE
), upon query the file is decrypted to a temp file. The temp file will be deleted afterwards. This is a crude clones of pash, written since I don’t want my id’s to be exposed as the names of the .gpg
files. You will be able to upload the PASSWORD_STORE
file to you e.g. github repository without exposing any of your account information. For instance, mine
1-----BEGIN PGP MESSAGE-----
2
3hQIMA21DdFXR20c9AQ/9FKArzUEDuN4YWBeINf+xiFePyx5V/rsAlSq+sQVs70Qb
4zxXu8fyL5iD/2YO7VLZdho/VOv1KeowtoANv5Ntb587SsUVxX5uhL/XQTy3BGj8f
50AY8vMG4yzn5Njr/INkClxS5uPYcXN2/bNKz/fydNqJ4GTpb9a5L1SRYQLbqtFow
6rACv4C2YYttrW0XRRR48kohpFw9MaQ/i8Dq7Xo+LvIOWLodHf/AtGKJ/33TCplm5
7spG2p3Z/dnYcTcYL3XhiF/jMsPZY/P9UWDHvkUK6EFCUx2EVGe1d6G+rsQF+CH7a
8kE8QJsbZbQATvIfBDNxFtn3PKAK3VKChIEOWWWDorHa2JaqDjYMe1q1cQa7tb/Wj
9hkmI77zb9Uy7UNJKs4J8AGF8ButGbPptB0zxAStneJnKeiA2xb1ud8zwe+rG07pE
10XJcAKm23J1COU6rlk5p0i3iKPmBMnZ/zmidMXXB9iWFVSLrgnaDI0z+Jwqo//jLv
11ybkUlHxCMIjaW9YVtfaal2iEK09O84FeC59hY3/yHsV8whcQX3/fVjdeykjMZacT
12lKG79OTImAvzLYWOxcFjC1vBxK5qaOhRoTrzKvycoKWQHrmcDfPQV+tPA+9sNYPU
13VYEfIS8K7vNODvO7giL5jywbg0JPMh9EuVoQMi6G9nmZAV8lyVeG0QoNiy3SA5jS
14wQgBIvIOBfuPxhkdrH8ihGlZqWKDXawB6cUaYbp2ROSro2ucYmUW8MUdMCCaa6lf
15ucEBSygZWCAyGITxTsk9XVHXqKkiFzZOn3S/4hWmerNSdZO95eDz7l+QSGkTwQoZ
16Ykk6U6JWt7JQOb1fW4qjm3BLpkS170QD6aIOrsJZYxtNSEPDw4624gpat8lMRSTD
17oJkB9nPmQ+8ksc3ds0fFdAd4V6Yq5GQ3uG7XzwZgb3NyRLJ8vzHQ4vdGrEi3AT+n
18argR0S2N+qkMGGlNe8RX4VVslyf/dkzbRKg+jokw8Z3gULXE+mjeZGxTRYVNdkId
19CrRFa+bQ5ptjsfJkREoPIj/8XiFOJbnbtpmEIdi8GfChrYPhsa+PS7y22HNXsuPH
20/aRylJvAnP9rHc+1URSMFwYvMwtMwQClMfEFoBwc5jZIUUG+uUzFkL/cJqxtgQ0E
21vPLL9+NECg8oAOd29k8uEOuOGlly1038sWohohak7+0MXWMO6GZwonWspFONGeMF
22sUKhB8mVh5vcuhyROlFYi97MDJI7abbpm/cGhcHm2fsHwSK5rAQK1keb033rx+tj
23nuvFC45MKQEvf4//DrCgKd1oBmSqtGbybQg=
24=dune
25-----END PGP MESSAGE-----
password
for query.password n(ew)
to add new account. Passwords will be randomly generated.password e(dit)
to edit the account information manually, e.g. to modify the password for already-existing accounts.password h
for help.
Note that
- The script utilizes
xclip
to write passwords to the clipboard. The clipboard will be cleared after 15 seconds. You should modified the functionpass_copy()
if you don’t want this behaviour. - When modifying passwords manually it would be better to avoid symbols like
"
. - When the script is executed through commands such as
xterm -e
, you need to use the commentedpass_copy()
function since the script will fail to write passowords to the clipboard.
1#!/usr/bin/env bash
2
3set -Eeuo pipefail
4
5BIN_NAME=${0##*/}
6MENU=${PASSWORD_MENU:-fzf}
7PASSWORD_STORE=${PASSWORD_STORE:-$HOME/.password}
8GREPPER=${PASSWORD_GREPPER:-grep}
9EDITOR=${EDITOR:-vim}
10PASS_LENGTH=${PASSWORD_LENGTH:-14}
11PASS_COPY=${PASS_COPY:=xclip -selection clipboard}
12PASS_TIMEOUT_SEC=${PASS_TIMEOUT:=15}
13
14
15umask 077
16TMP=`mktemp`
17trap "[ -n "$TMP" ] && rm -f $TMP" EXIT SIGHUP SIGINT SIGQUIT SIGKILL
18
19die(){
20 echo "$@" >&2
21 exit 1
22}
23
24pass_copy(){
25 echo "Clearing clipboard in $PASS_TIMEOUT_SEC seconds."
26 {
27 sleep "$PASS_TIMEOUT_SEC" || kill 0
28 setsid $PASS_COPY </dev/null
29 } &
30 $PASS_COPY <<< "$1"
31}
32
33#
34#pass_copy(){
35# $PASS_COPY <<< "$1"
36# echo "Clearing clipboard in $PASS_TIMEOUT_SEC seconds."
37# setsid clearclip $PASS_TIMEOUT_SEC &
38#}
39#
40# with
41#
42# clearclip
43#
44# #!/usr/bin/env sh
45#
46# sleep $1
47# xsel -c -b
48
49
50get_pass(){
51 gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
52 local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
53 [ -z "$chosen" ] && die "Nothing chosen"
54 local pass=$($GREPPER ^"$chosen" < $TMP | awk '{print $4}' )
55 pass_copy "$pass"
56}
57
58get_username(){
59 gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
60 local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
61 [ -z "$chosen" ] && die "Nothing chosen"
62 local name=$($GREPPER ^"$chosen" < $TMP | awk '{print $3}' )
63 pass_copy "$name"
64}
65
66get_all(){
67 gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
68 local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
69 [ -z "$chosen" ] && die "Nothing chosen"
70 local all=$($GREPPER ^"$chosen" < $TMP | awk '{print $3"\n"$4}' )
71 echo $all | awk '{print $1}'
72 local pass="$(echo $all | awk '{print $2}')"
73 pass_copy "$pass"
74}
75
76edit_pass(){
77 gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
78 $EDITOR $TMP
79 gpg -ae < $TMP > $PASSWORD_STORE
80}
81
82show(){
83 gpg -d $PASSWORD_STORE 2>/dev/null
84}
85
86gen_pass(){
87 LC_ALL=C </dev/urandom tr -dc '1234567890!@#$%^&*(){}[]qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' | head -c$PASS_LENGTH ; echo ""
88}
89
90
91new_pass(){
92 gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
93 local uid
94 local name
95 local tag
96 local pass
97 local addi
98 read -p "Tag (without space):" tag
99 [ -z "$tag" ] && die "No tag specified"
100 [ -n "$(echo $tag | awk '{print $2}')" ] && die "No space allowed"
101 echo
102 read -p "Username (without space) :" name
103 [ -z "$name" ] && die "No username specified"
104 [ -n "$(echo $name | awk '{print $2}')" ] && die "No space allowed"
105 echo
106 read -p "Additional info (only the first word will be printed when querying):" addi
107 echo
108 pass=$(gen_pass)
109 uid=$(echo "$tag $name $pass $addi" | md5sum | head -c5)
110 echo "$uid $tag $name $pass $addi" >> $TMP
111 gpg -ae < $TMP > $PASSWORD_STORE
112 echo "New account information stored as"
113 echo "$uid $tag $name PASSWORD $addi"
114 pass_copy "$pass" && echo "Password copied to the clipboard"
115}
116
117help(){
118cat <<EOF
119password file format:
120 UID TAG USERNAME PASSWORD ADDITIONAL_INFO
121 - Ensure that UIDs do not coincide when editing the password file by hand.
122 - Except for the last one, all the fields should be free from blank spaces.
123 - Only the first column of the ADDITIONAL_INFO field will be printed when querying.
124 - To check all the information stored use $BIN_NAME show | grep ^<UID>.
125
126 $BIN_NAME h[elp] Show this help message
127 $BIN_NAME e[dit] Edit the password file
128 $BIN_NAME a[ll] Get username and password (username will be printed and password will be copied)
129 $BIN_NAME i[d] Get username
130 $BIN_NAME n[ew] Add a new account
131 $BIN_NAME s[how] Decrypt and show the password-store file
132 $BIN_NAME Get password
133EOF
134}
135
136case ${1-default} in
137 e*) edit_pass ;;
138 i*) get_username ;;
139 a*) get_all ;;
140 n* ) new_pass ;;
141 s* ) show ;;
142 "default" ) get_pass ;;
143 * ) help ;;
144esac