pss

git clone https://orangeshoelaces.net/git/pss.git

/pss

   1 #!/bin/sh
   2 
   3 # Passwords, secrets and stuff
   4 # Copyright 2018, 2019 Vasilii Kolobkov
   5 # 
   6 # This program is free software: you can redistribute it and/or modify
   7 # it under the terms of the GNU General Public License as published by
   8 # the Free Software Foundation, either version 3 of the License, or
   9 # (at your option) any later version. 
  10 #
  11 # This program is distributed in the hope that it will be useful,
  12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 # GNU General Public License for more details.
  15 #
  16 # You should have received a copy of the GNU General Public License
  17 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
  18 
  19 umask 077
  20 
  21 # Secrets file is made of newline-separated records of following form:
  22 #     <name>FS<password>FS<resource>FS<notes>
  23 # where FS is value of 'field_sepator' variable.
  24 # Empty lines, ones made of whitespace and those beginning with '#' are ignored.
  25 
  26 secrets=~/.secrets
  27 field_separator="$(printf '\037')"
  28 
  29 usage() {
  30         echo 'Usage: pss [-n|-yn|-p|-yp] <key>|-a' 1>&2
  31         exit 1
  32 }
  33 
  34 prompt() {
  35         if [ -t 0 ]; then
  36                 printf '%s: ' "${1}"
  37         fi
  38 }
  39 
  40 read_field() {
  41         [ -t 0 -a "${1}" -eq 1 ] && stty -echo
  42         (
  43                 IFS= read -r val
  44                 printf '%s\n' "${val}" | tr -d "${field_separator}"
  45         )
  46         [ -t 0 -a "${1}" -eq 1 ] && stty echo
  47 }
  48 
  49 add_rec() {
  50         (       
  51                 prompt 'Name'; name="$(read_field 0)"
  52                 prompt 'Password'; password="$(read_field 1)"
  53                 prompt 'Resource'; resource="$(read_field 0)"
  54                 prompt 'Notes'; notes="$(read_field 0)"
  55 
  56                 set -eC
  57 
  58                 secrets_new="${secrets}.new"
  59                 secrets_old="${secrets}.old"
  60                 {
  61                         [ -f "${secrets}" ] && gpg --decrypt "${secrets}"
  62                         {
  63                                 echo "${name}"
  64                                 echo "${password}"
  65                                 echo "${resource}"
  66                                 echo "${notes}"
  67                         } | pr -4 -t -s"${field_separator}"
  68                 } | gpg --encrypt --default-recipient-self > "${secrets_new}"
  69                 [ -f "${secrets}" ] && mv "${secrets}" "${secrets_old}"
  70                 mv "${secrets_new}" "${secrets}"
  71                 [ -f "${secrets_old}" ] && unlink "${secrets_old}"
  72         )
  73 }
  74 
  75 find_rec() {
  76         awk -F "${field_separator}" -v "query=${1}" '
  77                 $0 ~ /^#/ || $0 ~ /^[:space:]*$/ { next }
  78                 $1 == query || $3 == query {
  79                         rec = $0
  80                         nexact++
  81                 }
  82                 index($1, query) || index($3, query) {
  83                         rec = $0
  84                         nsubstr++
  85                 }
  86                 END {
  87                         if (nexact == 1 || (nexact == 0 && nsubstr == 1)) print rec 
  88                         else if (nexact + nsubstr == 0) exit 1
  89                         else exit 2
  90                 }'
  91         case "${?}" in
  92                 1) echo 'No match' 1>&2; return 1;;
  93                 2) echo 'Ambiguous query' 1>&2; return 2;;
  94         esac
  95 }
  96 
  97 show_field() {
  98         if [ "${1}" = "" ]; then
  99                 usage
 100         fi
 101         gpg --decrypt "${secrets}" | find_rec "${1}" |
 102         awk -F "${field_separator}" "{print \$${2}}"
 103 }
 104 
 105 if [ ${#} -lt 1 ]; then
 106         usage
 107 fi
 108 
 109 case "${1}" in
 110         -n) shift; show_field "${*}" 1;;
 111         -yn) shift; show_field "${*}" 1 | xclip -r;;
 112         -p) shift; show_field "${*}" 2;;
 113         -yp) shift; show_field "${*}" 2 | xclip -r;;
 114         -a) add_rec;;
 115         *) usage;;
 116 esac