pass

- posix password manager 🔒
git clone git://git.acid.vegas/pass.git
Log | Files | Refs | Archive | README | LICENSE

pass (4060B)

      1 #!/bin/sh
      2 # posix password manager - developed by acidvegas (https://git.acid.vegas/pass)
      3 umask 077
      4 export GPG_TTY=$(tty)
      5 
      6 GPG_ID="acidvegas" # change me
      7 GPG_OPTS="-q --yes --compress-algo=none --no-encrypt-to --batch"
      8 METHOD="type"
      9 PASS_DIR=$HOME/.secrets
     10 
     11 if [ -z $EDITOR ]; then
     12 	export EDITOR=nano
     13 fi
     14 
     15 mkdir -p $PASS_DIR
     16 
     17 edit() {
     18 	template="pw.XXXXXXXXXXXXX"
     19 	if [ -d /dev/shm ] && [ -w /dev/shm ] && [ -x /dev/shm ]; then
     20 		tmp=$(mktemp /dev/shm/$template)
     21 	elif [ ! -z $PREFIX ] && [ -d $PREFIX/tmp ]; then
     22 		tmp=$(mktemp $PREFIX/usr/tmp/$template) # For users on Termux
     23 	else
     24 		echo "warning: /dev/shm does not exist or is missing permissions required for temporary files (using insecure fallback to /tmp directory)"
     25 		tmp=$(mktemp /tmp/$template)
     26 	fi
     27 	rm_tmp() {
     28 		rm -rf "$tmp"
     29 	}
     30 	trap rm_tmp EXIT
     31 	if [ -f $PASS_DIR/$1.gpg ]; then
     32 		gpg -d -o $tmp $GPG_OPTS $PASS_DIR/$1.gpg || exit 1
     33 		$EDITOR $tmp
     34 		if [ ! "$(gpg -d $GPG_OPTS $PASS_DIR/$1.gpg)" = "$(cat $tmp)" ]; then
     35 			gpg -e -r $GPG_ID -o $PASS_DIR/$1.gpg $GPG_OPTS $tmp
     36 		fi
     37 	else
     38 		$EDITOR $tmp
     39 		if [ -f $tmp ]; then
     40 			mkdir -p $(dirname $PASS_DIR/$1)
     41 			gpg -e -r $GPG_ID -o $PASS_DIR/$1.gpg $GPG_OPTS $tmp
     42 		fi
     43 	fi
     44 }
     45 
     46 generate() {
     47 	case ${1#[-+]} in
     48 		*[!0-9]* | '') echo "error: invalid number" ;;
     49 		*)             cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $1 | head -n 1 ;;
     50 	esac
     51 }
     52 
     53 menu() {
     54 	cwd=$PASS_DIR
     55 	while :
     56 	do
     57 		if [ $cwd = $PASS_DIR ]; then
     58 			cmd=$(ls -p $cwd | sed 's/\.gpg$//' | dmenu -l 5 "$@")
     59 		else
     60 			cmd=$({ echo ".."; ls -p $cwd | sed 's/\.gpg$//'; } | dmenu -l 5 "$@")
     61 		fi
     62 		if [ -z $cmd ]; then
     63 			break
     64 		elif [ $cmd = '..' ]; then
     65 			cwd=$(dirname $cwd)
     66 		elif [ -d $cwd/$cmd ]; then
     67 			cwd=$cwd/$cmd
     68 		elif [ -f $cwd/$cmd ]; then
     69 			if [ $METHOD = "copy" ]; then
     70 				export PINENTRY_USER_DATA="dmenu" | gpg -d $GPG_OPTS $cwd/$cmd | dmenu "$@" | xclip -selection clipboard
     71 				sleep 3 && xclip -selection clipboard /dev/nul
     72 			elif [ $METHOD = "type" ]; then
     73 				export PINENTRY_USER_DATA="dmenu" | gpg -d $GPG_OPTS $cwd/$cmd | dmenu "$@" | xdotool type --delay 3 "$D"
     74 			fi
     75 			break
     76 		fi
     77 	done
     78 }
     79 
     80 otp() {
     81 	if [ -f $PASS_DIR/$1.gpg ]; then
     82 		otp_uri=$(gpg -d $GPG_OPTS $PASS_DIR/$1.gpg | tail -n 1) || exit 1
     83 		if [ "$(echo $otp_uri | head -c 10)" = "otpauth://" ]; then
     84 			secret=$(echo "$otp_uri" | sed 's/.*secret=//' | cut -f1 -d'&')
     85 			oathtool -b --totp $secret
     86 		else
     87 			echo "error: OTP URI invalid or not found for '$1'"
     88 		fi
     89 	else
     90 		echo "error: '$1' does not exist"
     91 	fi
     92 }
     93 
     94 show() {
     95 	if [ -d $PASS_DIR/$1 ]; then
     96 		echo $1
     97 		tree -NCl --noreport $PASS_DIR/$1 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
     98 	elif [ -f $PASS_DIR/$1.gpg ]; then
     99 		gpg -d $GPG_OPTS $PASS_DIR/$1.gpg
    100 	else
    101 		echo "error: '$1' does not exist"
    102 	fi
    103 }
    104 
    105 # Main
    106 command -v gpg  >/dev/null || { echo "error: missing required package 'gpg'";  exit 1; }
    107 command -v tree >/dev/null || { echo "error: missing required package 'tree'"; exit 1; }
    108 if [ "$#" = '1' ]; then
    109 	if [ $1 = 'menu' ]; then
    110 		command -v dmenu          >/dev/null || { echo "error: missing required package 'dmenu'";          exit 1; }
    111 		command -v pinentry       >/dev/null || { echo "error: missing required package 'pinentry'";       exit 1; }
    112 		command -v pinentry-dmenu >/dev/null || { echo "error: missing required package 'pinentry-dmenu'"; exit 1; }
    113 		if [ $METHOD = "copy" ]; then
    114 			command -v dmenu >/dev/null || { echo "error: missing required package 'xclip'"; exit 1; }
    115 		elif [ $METHOD = 'type' ]; then
    116 			command -v xdotool >/dev/null || { echo "error: missing required package 'xdotool'"; exit 1; }
    117 		else
    118 			echo "error: invalid menu method (must be 'copy' or 'type')"
    119 			exit 1
    120 		fi
    121 		menu
    122 	else
    123 		show $1
    124 	fi
    125 elif [ "$#" = '2' ]; then
    126 	if [ "$1" = 'edit' ]; then
    127 		edit $2
    128 	elif [ "$1" = 'gen' ]; then
    129 		generate $2
    130 	elif [ "$1" = 'otp' ]; then
    131 		command -v oathtool >/dev/null || { echo "error: missing required package 'oathtool'"; exit 1; }
    132 		otp $2
    133 	fi
    134 else
    135 	tree -NCl --noreport $PASS_DIR 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
    136 fi