commit 5426e53dafb0ec4a4af33c880d4dd6e7fa024243 Author: acidvegas Date: Sat May 13 01:00:40 2023 -0400 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..016e197 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2023, acidvegas + +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 new file mode 100644 index 0000000..d2f9e5f --- /dev/null +++ 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 ` | Display password information for `` or a directory tree of stored passwords if `` 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 ` | Display stored password information for `` | +| `pass gen ` | Generate a random password that is `` characters long | +| `pass otp ` | Return a 2-Factor-Authenticaion code for `` *(Last line of `` must be a valid otpauth:// URI)* | + +###### Note +`` 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 `` + +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 new file mode 100755 index 0000000..9e5710f --- /dev/null +++ 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