pass

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

commit 5426e53dafb0ec4a4af33c880d4dd6e7fa024243
Author: acidvegas <acid.vegas@acid.vegas>
Date: Sat, 13 May 2023 01:00:40 -0400

Initial commit

Diffstat:
ALICENSE | 15+++++++++++++++
AREADME.md | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apass | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3 files changed, 213 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2023, acidvegas <acid.vegas@acid.vegas>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,64 @@
+# pass
+> posix password manager
+
+A very simple password manager that keeps passwords inside gpg encrypted files inside a simple directory tree.
+
+Similar to [password-store](https://git.zx2c4.com/password-store/about/), but written in POSIX compliant shell script instead of bash.
+
+## Requirements
+- [gpg](https://gnupg.org/)
+- tree
+
+###### Optional Requirements
+- [nano](https://www.nano-editor.org/)                      *(required only if environment variable `$EDITOR` is not set)*
+- [dmenu](https://tools.suckless.org/dmenu/)                *(required for menu)*
+- [pinentry-dmenu](https://github.com/ritze/pinentry-dmenu) *(required for menu)*
+- [xclip](https://github.com/astrand/xclip)                 *(required for menu to copy passwords)*
+- [xdotool](https://github.com/jordansissel/xdotool)        *(required for menu to type passwords)*
+- [oath-toolit](https://www.nongnu.org/oath-toolkit/)       *(required for 2FA)*
+
+## Config
+Edit the source code to change these settings:
+
+| Setting  | Description                                                                                                        |
+| -------- | ------------------------------------------------------------------------------------------------------------------ |
+| `GPG_ID`   | Default GPG key ID to use for encrypting/decrypting                                                              |
+| `GPG_OPTS` | Do not edit this unless you know what you are doing                                                              |
+| `METHOD`   | Method used for the menu *("copy" will use xclip to copy passwords & "type" will use xdotool to type passwords)* |
+| `PASS_DIR` | Directory to store all password information                                                                      |
+
+## Usage
+| Command            | Description                                                                                                  |
+| ------------------ | ------------------------------------------------------------------------------------------------------------ |
+| `pass`             | Display a directory tree of stored passwords                                                                 |
+| `pass <path>`      | Display password information for `<path>` or a directory tree of stored passwords if `<path>` is a directory |
+| `pass menu`        | Use pass in dmenu *(Selected line is copied to the clipboard or typed out depending on the `METHOD` used)*   |
+| `pass edit <path>` | Display stored password information for `<path>`                                                             |
+| `pass gen <len>`   | Generate a random password that is `<len>` characters long                                                   |
+| `pass otp <path>`  | Return a 2-Factor-Authenticaion code for `<path>` *(Last line of `<path>` must be a valid otpauth:// URI)*   |
+
+###### Note
+`<path>` is not a direct path per-say. If the password is stored in `$PASS_DIR/www/github.gpg` all you have to put is `www/github` for `<path>`
+
+When using the menu, the clipboard is cleared after 3 seconds or passwords are typed after 3 seconds, depending on what `METHOD` you set in the config.
+
+For setting up 2FA, you can download the QR code image & use [zbar](https://github.com/mchehab/zbar) to convert it to a string to get a valid URI.
+
+## Pinentry Setup
+To keep everything in the command line, make sure you edit your `$HOME/.gnupg/gpg-agent.conf` to include `pinentry-program /usr/bin/pinentry-curses`
+
+If you plan on using the menu features, [pinentry-dmenu](https://github.com/ritze/pinentry-dmenu) will allow you to enter your GPG key password inside of dmenu, but in order to do that you will need to create a wrapper for pinetry at `$HOME/.gnupg/pinentry-wrapper`:
+```
+if [ "$PINENTRY_USER_DATA" = "dmenu" ]; then
+    exec /usr/local/bin/pinentry-dmenu "$@"
+else
+    exec /usr/bin/pinentry-curses "$@"
+fi
+```
+Make it executable with `chmod +x $HOME/.gnupg/pinentry-wrapper` and then edit your `$HOME/.gnupg/gpg-agent.conf` to include `pinentry-program $HOME/.gnupg/pinentry-wrapper`.
+
+## Mirrors
+- [acid.vegas](https://git.acid.vegas/pass)
+- [GitHub](https://github.com/acidvegas/pass)
+- [GitLab](https://gitlab.com/acidvegas/pass)
+- [SuperNETs](https://git.supernets.org/acidvegas/pass)
+\ No newline at end of file
diff --git a/pass b/pass
@@ -0,0 +1,132 @@
+#!/bin/sh
+# posix password manager - developed by acidvegas (https://git.acid.vegas/pass)
+
+umask 077
+
+export GPG_TTY=$(tty)
+
+GPG_ID="acidvegas" # change me
+GPG_OPTS="-q --yes --compress-algo=none --no-encrypt-to --batch"
+METHOD="copy"
+PASS_DIR=$HOME/.secrets
+
+if [ -z $EDITOR ]; then
+	export EDITOR=nano
+fi
+
+mkdir -p $PASS_DIR
+
+edit() {
+	if [ -d /dev/shm ] && [ -w /dev/shm ] && [ -x /dev/shm ]; then
+		tmp=$(mktemp -u /dev/shm/pw.XXXXXXXXXX)
+		trap "rm -rf $tmp" EXIT
+	else
+		echo "error: /dev/shm does not exist or is missing permissions required for temporary files"
+		exit 1
+	fi
+	if [ -f $PASS_DIR/$1.gpg ]; then
+		gpg -d -o $tmp $GPG_OPTS $PASS_DIR/$1.gpg || exit 1
+		$EDITOR $tmp
+		if [ ! "$(gpg -d $GPG_OPTS $PASS_DIR/$1.gpg)" = "$(cat $tmp)" ]; then
+			gpg -e -r $GPG_ID -o $PASS_DIR/$1.gpg $GPG_OPTS $tmp
+		fi
+	else
+		$EDITOR $tmp
+		if [ -f $tmp ]; then
+			mkdir -p $(dirname $PASS_DIR/$1)
+			gpg -e -r $GPG_ID -o $PASS_DIR/$1.gpg $GPG_OPTS $tmp
+		fi
+	fi
+}
+
+generate() {
+	case ${1#[-+]} in
+		*[!0-9]* | '') echo "error: invalid number" ;;
+		*)             cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $1 | head -n 1 ;;
+	esac
+}
+
+menu() {
+	cwd=$PASS_DIR
+	while :
+	do
+		if [ $cwd = $PASS_DIR ]; then
+			cmd=$(ls -p $cwd | dmenu "$@")
+		else
+			cmd=$($(echo ".." && ls -p $cwd) | dmenu "$@")
+		fi
+		if [ -z $cmd ]; then
+			break
+		elif [ $cmd = '..' ]; then
+			cwd=$(dirname $cwd)
+		elif [ -d $cwd/$cmd ]; then
+			cwd=$cwd/$cmd
+		elif [ -f $cwd/$cmd ]; then
+			if [ $METHOD = "copy" ]; then
+				export PINENTRY_USER_DATA="dmenu" | gpg -d $GPG_OPTS $cwd/$cmd | dmenu "$@" | xclip -selection clipboard
+				sleep 3 && xclip -selection clipboard /dev/null
+			elif [ $METHOD = "type" ]; then
+				export PINENTRY_USER_DATA="dmenu" | gpg -d $GPG_OPTS $cwd/$cmd | dmenu "$@" | xdotool type --delay 3 "$D"
+			fi
+			break
+		fi
+	done
+}
+
+otp() {
+	if [ -f $PASS_DIR/$1.gpg ]; then
+		otp_uri=$(gpg -d $GPG_OPTS $PASS_DIR/$1.gpg | tail -n 1) || exit 1
+		if [ "$(echo $otp_uri | head -c 10)" = "otpauth://" ]; then
+			secret=$(echo "$otp_uri" | sed 's/.*secret=//' | cut -f1 -d'&')
+			oathtool -b --totp $secret
+		else
+			echo "error: OTP URI invalid or not found for '$1'"
+		fi
+	else
+		echo "error: '$1' does not exist"
+	fi
+}
+
+show() {
+	if [ -d $PASS_DIR/$1 ]; then
+		echo $1
+		tree -NCl --noreport $PASS_DIR/$1 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
+	elif [ -f $PASS_DIR/$1.gpg ]; then
+		gpg -d $GPG_OPTS $PASS_DIR/$1.gpg
+	else
+		echo "error: '$1' does not exist"
+	fi
+}
+
+# Main
+[ ! $(command -v gpg)  ] && echo "error: missing required packaged 'gpg'"  && exit 1
+[ ! $(command -v tree) ] && echo "error: missing required packaged 'tree'" && exit 1
+if [ "$#" = '1' ]; then
+	if [ $1 = 'menu' ]; then
+		[ ! $(command -v dmenu)          ] && echo "error: missing required packaged 'dmenu'"          && exit 1
+		[ ! $(command -v pinentry)       ] && echo "error: missing required packaged 'pinentry'"       && exit 1
+		[ ! $(command -v pinentry-dmenu) ] && echo "error: missing required packaged 'pinentry-dmenu'" && exit 1
+		if [ $METHOD = "copy" ]; then
+			[ ! $(command -v xclip)      ] && echo "error: missing required packaged 'xclip'"          && exit 1
+		elif [ $METHOD = 'type' ]; then
+			[ ! $(command -v xdotool)    ] && echo "error: missing required packaged 'xdotool'"        && exit 1
+		else
+			echo "error: invalid menu method (must be 'copy' or 'type')"
+			exit 1
+		fi
+		menu
+	else
+		show $1
+	fi
+elif [ "$#" = '2' ]; then
+	if [ "$1" = 'edit' ]; then
+		edit $2
+	elif [ "$1" = 'gen' ]; then
+		generate $2
+	elif [ "$1" = 'otp' ]; then
+		[ ! $(command -v oathtool) ] && echo "error: missing required packaged 'oathtool'" && exit 1
+		otp $2
+	fi
+else
+	tree -NCl --noreport $PASS_DIR 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
+fi
+\ No newline at end of file