init sources
This commit is contained in:
parent
c74cd2d662
commit
d7c91472e6
14 changed files with 679 additions and 1 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Zmakefile
|
||||||
20
Makefile
Normal file
20
Makefile
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
var_exclude = ZPASS_.* XDG_.* REMOTE_.* DISPLAY CONFIGFILE TMPDIR
|
||||||
|
fct_exclude = _tty_on
|
||||||
|
|
||||||
|
zpass: src/*
|
||||||
|
lxsh -o zpass -m --minimize-var --exclude-var "$(var_exclude)" --minimize-fct --exclude-fct "$(fct_exclude)" --remove-unused src/main.sh
|
||||||
|
|
||||||
|
debug: src/*
|
||||||
|
lxsh -o zpass src/main.sh
|
||||||
|
|
||||||
|
build: zpass
|
||||||
|
|
||||||
|
install: build
|
||||||
|
mv zpass /usr/local/bin
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm /usr/local/bin/zpass
|
||||||
|
|
||||||
|
clear:
|
||||||
|
rm zpass
|
||||||
83
README.md
83
README.md
|
|
@ -1,2 +1,83 @@
|
||||||
# zpass
|
# zpass
|
||||||
Basic and simple password management
|
|
||||||
|
Basic and simple password management for UNIX using shell. <br>
|
||||||
|
Does not require any setup, only uses a password to encrypt the archive.
|
||||||
|
|
||||||
|
Systems other than GNU/linux are untested at the moment
|
||||||
|
|
||||||
|
# Installing
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
- gpg
|
||||||
|
- tar
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
- screen (key caching and clipboard time)
|
||||||
|
- openssh (for remote files)
|
||||||
|
- zenity (GUI prompt)
|
||||||
|
- kdialog (better GUI prompt in KDE)
|
||||||
|
- xclip (copy on X)
|
||||||
|
- wl-clipboard (copy on wayland)
|
||||||
|
|
||||||
|
## Prebuilt
|
||||||
|
|
||||||
|
From [zpkg](https://github.com/zawwz/zpkg) package repository
|
||||||
|
|
||||||
|
## From source
|
||||||
|
|
||||||
|
Requires [lxsh](https://github.com/zawwz/lxsh)
|
||||||
|
|
||||||
|
Clone this repository then run `make install`
|
||||||
|
|
||||||
|
# Use
|
||||||
|
|
||||||
|
By design zpass uses encrypted archive files, wherein a file contains a value.
|
||||||
|
You can use predefined operations, or perform custom executions inside the archive.
|
||||||
|
|
||||||
|
See `zpass -h` for information on operations and configuration
|
||||||
|
|
||||||
|
When using `get` or `copy`, if the path entered is a folder,
|
||||||
|
zpass will look for a `default` file in this folder
|
||||||
|
|
||||||
|
## Example use
|
||||||
|
|
||||||
|
Create a file with `zpass c`.
|
||||||
|
A prompt will appear to use a password to encrypt the password archive file.
|
||||||
|
<b>If you lose this password, you lose access to all contents of the archive</b>.
|
||||||
|
|
||||||
|
You can create new values with either `zpass add <path...>`, `zpass new <path...>`, or `zpass set <path> <value>`
|
||||||
|
|
||||||
|
To copy a value into the clipboard, use `zpass <value>` or `zpass copy <value>`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
zpass will load by default the file `.config/zpass/default.conf` in your home directory
|
||||||
|
|
||||||
|
### Configuring remote file
|
||||||
|
|
||||||
|
You can configure zpass to use a file on a remote server.
|
||||||
|
You need SSH access to the target machine.<br>
|
||||||
|
Here is an example configuration:
|
||||||
|
```
|
||||||
|
ZPASS_REMOTE_ADDR=user@example.com
|
||||||
|
ZPASS_SSH_ID=~/.ssh/id_rsa
|
||||||
|
```
|
||||||
|
|
||||||
|
### Making the cache volatile
|
||||||
|
|
||||||
|
If you are caching keys, by default zpass uses `~/.cache` as a caching path.
|
||||||
|
This can be troublesome in case the machine stops before the cache timer runs out,
|
||||||
|
leaving a file containing the key in plaintext. <br>
|
||||||
|
This can be fixed by pointing the cache path to a volatile filesystem. <br>
|
||||||
|
For example:
|
||||||
|
```
|
||||||
|
ZPASS_CACHE_PATH=/tmp/zpasscache
|
||||||
|
```
|
||||||
|
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
### Prompt keeps appearing even with correct password
|
||||||
|
|
||||||
|
Make sure your gpg configuration is correct, you can run `gpg -c < /dev/null` to check
|
||||||
|
|
|
||||||
101
src/archive.sh
Normal file
101
src/archive.sh
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# $1 = tmpdir , $2 = keyfile
|
||||||
|
unpack() {
|
||||||
|
rm -rf "$1" || return $?
|
||||||
|
mkdir -p "$1" || return $?
|
||||||
|
(
|
||||||
|
set -e
|
||||||
|
cd "$1"
|
||||||
|
decrypt "$2" | tar -xf - 2>/dev/null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = tmpdir , $2 = keyfile
|
||||||
|
pack()
|
||||||
|
{
|
||||||
|
# clean empty dirs
|
||||||
|
archive="archive_$(randalnum 5)"
|
||||||
|
(
|
||||||
|
cd "$1" || exit $?
|
||||||
|
if [ -n "$2" ]
|
||||||
|
then
|
||||||
|
key=$(cat "$2") && rm "$2" || exit $?
|
||||||
|
else
|
||||||
|
key=$(new_key_with_confirm) || exit $?
|
||||||
|
fi
|
||||||
|
tar -cf - -- * | encrypt "$key" > "$1/$archive" || exit $?
|
||||||
|
) || return $?
|
||||||
|
if [ -n "$ZPASS_REMOTE_ADDR" ]
|
||||||
|
then
|
||||||
|
[ -z "$ZPASS_PATH" ] && datapath="~/.local/share/zpass"
|
||||||
|
if [ -n "$ZPASS_SSH_ID" ]
|
||||||
|
then scp -i "$ZPASS_SSH_ID" "$1/$archive" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
|
||||||
|
else scp "$1/$archive" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
|
||||||
|
fi
|
||||||
|
rm -f "$1/$archive" 2>/dev/null
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
mv -f "$1/$archive" "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $@ = command to execute inside archive
|
||||||
|
# set env __NOPACK to not repack after command
|
||||||
|
archive_exec()
|
||||||
|
{
|
||||||
|
err=0
|
||||||
|
# tmp files
|
||||||
|
tmpdir="$TMPDIR/zpass_$(randalnum 5)"
|
||||||
|
keyfile="$tmpdir/$(randalnum 5).key"
|
||||||
|
# operation
|
||||||
|
(
|
||||||
|
# unpack
|
||||||
|
unpack "$tmpdir/archive" "$keyfile" || exit $?
|
||||||
|
# execute
|
||||||
|
(cd "$tmpdir/archive" && "$@") || exit $?
|
||||||
|
# repack
|
||||||
|
[ -z "$__NOPACK" ] && { pack "$tmpdir/archive" "$keyfile" || exit $?; }
|
||||||
|
exit 0
|
||||||
|
) || err=$?
|
||||||
|
# cleanup
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
return $err
|
||||||
|
}
|
||||||
|
|
||||||
|
# no argument
|
||||||
|
create() {
|
||||||
|
if [ -f "$file" ]
|
||||||
|
then
|
||||||
|
tmpdir="$TMPDIR/zpass_$(randalnum 5)"
|
||||||
|
# pack n repack with no tmp key: create new
|
||||||
|
unpack "$tmpdir" || return $?
|
||||||
|
pack "$tmpdir" || { echo "Encryption error" >&2 && return 1 ; }
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
else
|
||||||
|
# if remote: file tmp
|
||||||
|
[ -n "$ZPASS_REMOTE_ADDR" ] && {
|
||||||
|
file="$TMPDIR/zpass_$(filehash)$ZPASS_EXTENSION"
|
||||||
|
}
|
||||||
|
# get key
|
||||||
|
[ -z "$ZPASS_KEY" ] && {
|
||||||
|
ZPASS_KEY=$(new_key_with_confirm) || { echo "Cancelled" >&2 && return 100 ; }
|
||||||
|
}
|
||||||
|
# create archive
|
||||||
|
tar -cf - -T /dev/null | encrypt "$ZPASS_KEY" > "$file" || {
|
||||||
|
echo "Encryption error" >&2
|
||||||
|
# echo "$tmperr" >&2
|
||||||
|
rm "$file"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[ -n "$ZPASS_REMOTE_ADDR" ] && {
|
||||||
|
ssh "$ZPASS_REMOTE_ADDR" "mkdir -p '$datapath'"
|
||||||
|
if [ -n "$ZPASS_SSH_ID" ]
|
||||||
|
then scp -i "$ZPASS_SSH_ID" "$file" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
|
||||||
|
else scp "$file" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
|
||||||
|
fi
|
||||||
|
rm -rf "$file" 2>/dev/null
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
38
src/cache.sh
Normal file
38
src/cache.sh
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
## Cache functions
|
||||||
|
|
||||||
|
clear_cache() {
|
||||||
|
rm "$cachepath"/*
|
||||||
|
}
|
||||||
|
|
||||||
|
write_cache() {
|
||||||
|
echo "$1" > "$cachepath/$(keyfile)"
|
||||||
|
delete_cache "$ZPASS_KEY_CACHE_TIME"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_key_cached() {
|
||||||
|
[ ! -f "$file" ] && return 0
|
||||||
|
cat "$cachepath/$(keyfile)" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = delay in sec
|
||||||
|
delete_cache() {
|
||||||
|
if [ "$1" -gt 0 ] 2>/dev/null
|
||||||
|
then
|
||||||
|
for I in $(screen -ls | grep "zpass_$(keyfile)" | awk '{print $1}')
|
||||||
|
do
|
||||||
|
screen -S "$I" -X stuff "^C"
|
||||||
|
done
|
||||||
|
screen -dmS "zpass_$(keyfile)" sh -c "sleep $1 ; $0 rmc" # call zpass with cache delete
|
||||||
|
else
|
||||||
|
rm -f "$cachepath/$(keyfile)" 2>/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_cache() {
|
||||||
|
# key cache
|
||||||
|
find "$cachepath" -type f ! -newermt @$(date -d "-$ZPASS_KEY_CACHE_TIME seconds" +%s) -print0 | xargs -0 rm
|
||||||
|
# tmp folders older than 5 min
|
||||||
|
rm -rd $(find "$TMPDIR" -maxdepth 1 -type d -name 'zpass_*' ! -mmin 5)
|
||||||
|
}
|
||||||
45
src/clipboard.sh
Normal file
45
src/clipboard.sh
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
clipboard()
|
||||||
|
{
|
||||||
|
unset in
|
||||||
|
read -r in
|
||||||
|
while read -r ln
|
||||||
|
do
|
||||||
|
in="$in
|
||||||
|
$ln"
|
||||||
|
done
|
||||||
|
printf "%s" "$in" | xclip -selection clipboard
|
||||||
|
which wl-copy >/dev/null 2>&1 && printf "%s" "$in" | wl-copy
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = delay in sec
|
||||||
|
clipboard_clear() {
|
||||||
|
if [ -n "$1" ]
|
||||||
|
then
|
||||||
|
for I in $(screen -ls | grep "$fname"_clipboard | awk '{print $1}')
|
||||||
|
do
|
||||||
|
screen -S "$I" -X stuff "^C"
|
||||||
|
done
|
||||||
|
screen -dmS "$fname"_clipboard sh -c "sleep $1
|
||||||
|
xclip -selection clipboard < /dev/null
|
||||||
|
which wl-copy 2>&1 && wl-copy < /dev/null
|
||||||
|
sleep 1"
|
||||||
|
else
|
||||||
|
echo | clipboard
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_check()
|
||||||
|
{
|
||||||
|
if ps -e | grep -qi wayland
|
||||||
|
then
|
||||||
|
which wl-copy >/dev/null 2>&1 || error 1 "ERROR: running wayland but wl-clipboard is not installed"
|
||||||
|
elif [ -n "$DISPLAY" ]
|
||||||
|
then
|
||||||
|
which xclip >/dev/null 2>&1 || error 1 "ERROR: running X but xclip is not installed"
|
||||||
|
else
|
||||||
|
error 1 "ERROR: no graphical server detected"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
46
src/config.sh
Normal file
46
src/config.sh
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# XDG config/cache
|
||||||
|
datapath=".local/share/zpass"
|
||||||
|
cachepath="$HOME/.cache/zpass"
|
||||||
|
configpath="$HOME/.config/zpass"
|
||||||
|
[ -n "$XDG_CONFIG_HOME" ] && configpath="$XDG_CONFIG_HOME/zpass"
|
||||||
|
[ -n "$XDG_CACHE_HOME" ] && cachepath="$XDG_CACHE_HOME/zpass"
|
||||||
|
[ -z "$CONFIGFILE" ] && CONFIGFILE="$configpath/default.conf"
|
||||||
|
[ -n "$XDG_DATA_HOME" ] && [ -z "$ZPASS_REMOTE_ADDR" ] && datapath="$XDG_DATA_HOME/zpass"
|
||||||
|
|
||||||
|
[ -z "$TMPDIR" ] && TMPDIR=/tmp
|
||||||
|
|
||||||
|
# stash env config
|
||||||
|
tmpenv="$TMPDIR/zpassenv_$(randalnum 5)"
|
||||||
|
env | grep '^ZPASS_.*=' | sed "s/'/'\\\''/g;s/=/='/;s/$/'/g" > "$tmpenv"
|
||||||
|
|
||||||
|
# load config file
|
||||||
|
[ -f "$CONFIGFILE" ] && { . "$CONFIGFILE" || exit $? ; }
|
||||||
|
|
||||||
|
. "$tmpenv" || exit $?
|
||||||
|
rm -f "$tmpenv" 2>/dev/null
|
||||||
|
|
||||||
|
# resolve zpass_path
|
||||||
|
[ -n "$ZPASS_PATH" ] && datapath="$ZPASS_PATH"
|
||||||
|
[ -n "$ZPASS_CACHE_PATH" ] && cachepath="$ZPASS_CACHE_PATH"
|
||||||
|
|
||||||
|
# default ZPASS config
|
||||||
|
[ -z "$ZPASS_FILE" ] && ZPASS_FILE=default
|
||||||
|
[ -z "$ZPASS_EXTENSION" ] && ZPASS_EXTENSION=.tar.gpg
|
||||||
|
[ -z "$ZPASS_KEY_CACHE_TIME" ] && ZPASS_KEY_CACHE_TIME=60 # in seconds
|
||||||
|
[ -z "$ZPASS_CLIPBOARD_TIME" ] && ZPASS_CLIPBOARD_TIME=30 # in seconds
|
||||||
|
[ -z "$ZPASS_UNK_OP_CALL" ] && ZPASS_UNK_OP_CALL=copy
|
||||||
|
[ -z "$ZPASS_RAND_LEN" ] && ZPASS_RAND_LEN=20
|
||||||
|
|
||||||
|
# datapath resolution
|
||||||
|
# remove tildes
|
||||||
|
datapath="${datapath#\~/}"
|
||||||
|
[ "$datapath" = '~' ] && datapath=""
|
||||||
|
# if not remote and not absolute: add HOME
|
||||||
|
[ -z "$ZPASS_REMOTE_ADDR" ] && [ "$(echo "$datapath" | cut -c1)" != '/' ] && datapath="$HOME/$datapath"
|
||||||
|
|
||||||
|
file="$datapath/$ZPASS_FILE$ZPASS_EXTENSION"
|
||||||
|
|
||||||
|
[ -z "$ZPASS_REMOTE_ADDR" ] && { mkdir -p "$datapath" 2>/dev/null || error 1 "Could not create '$datapath'"; }
|
||||||
|
mkdir -p "$cachepath" 2>/dev/null && chmod -R go-rwx "$cachepath" 2>/dev/null
|
||||||
56
src/crypt.sh
Normal file
56
src/crypt.sh
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# $1 = key
|
||||||
|
encrypt() {
|
||||||
|
gpg --pinentry-mode loopback --batch --passphrase "$1" -o - -c -
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = key , $2 = keyfile to write
|
||||||
|
decrypt_with_key()
|
||||||
|
{
|
||||||
|
gpg --pinentry-mode loopback --batch --passphrase "$1" -o - -d "$file" 2>/dev/null && ret=$? && [ -n "$2" ] && echo "$1" > "$2"
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = keyfile to write
|
||||||
|
decrypt()
|
||||||
|
{
|
||||||
|
# get remote file
|
||||||
|
[ -n "$ZPASS_REMOTE_ADDR" ] && {
|
||||||
|
file="$TMPDIR/zpass_$(filehash)$ZPASS_EXTENSION"
|
||||||
|
[ -z "$ZPASS_PATH" ] && datapath="~/.local/share/zpass"
|
||||||
|
if [ -n "$ZPASS_SSH_ID" ]
|
||||||
|
then scp -i "$ZPASS_SSH_ID" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" "$file" >/dev/null || return $?
|
||||||
|
else scp "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" "$file" >/dev/null || return $?
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
cat "$file" >/dev/null 2>&1 || { echo "File doesn't exist. Use 'zpass create' to create the file" >&2 && return 1; } # no file
|
||||||
|
|
||||||
|
if [ -n "$ZPASS_KEY" ]
|
||||||
|
then # key given already
|
||||||
|
decrypt_with_key "$ZPASS_KEY" "$1" ; ret=$?
|
||||||
|
else # prompt for key
|
||||||
|
# attempt decrypt from cache
|
||||||
|
key=$(get_key_cached) && decrypt_with_key "$key" "$1"
|
||||||
|
ret=$?
|
||||||
|
if [ $ret -ne 0 ]
|
||||||
|
then
|
||||||
|
# cache was incorrect: delete
|
||||||
|
delete_cache >/dev/null 2>&1
|
||||||
|
# try loop
|
||||||
|
tries=0
|
||||||
|
while [ $ret -ne 0 ] && [ $tries -lt 3 ]
|
||||||
|
do
|
||||||
|
key=$(ask_key) || { echo "Cancelled" >&2 && return 100 ; }
|
||||||
|
tries=$((tries+1))
|
||||||
|
decrypt_with_key "$key" "$1" ; ret=$?
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# remove temporary file
|
||||||
|
[ -n "$ZPASS_REMOTE_ADDR" ] && rm -rf "$file" 2>/dev/null
|
||||||
|
|
||||||
|
[ $ret -ne 0 ] && { echo "Could not decrypt '$file'" >&2 ; }
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
13
src/file.sh
Normal file
13
src/file.sh
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
list_files() {
|
||||||
|
_cmd_ "cd '$datapath' 2>/dev/null && find . -maxdepth 1 -type f -regex '.*$ZPASS_EXTENSION\$'" | sed 's/$(escape_chars "$ZPASS_EXTENSION")\$//g; s|.*/||g'
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_files()
|
||||||
|
{
|
||||||
|
for N
|
||||||
|
do
|
||||||
|
_cmd_ "rm '$datapath/$N$ZPASS_EXTENSION'" || exit $?
|
||||||
|
done
|
||||||
|
}
|
||||||
46
src/help.sh
Normal file
46
src/help.sh
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
fname=$(basename "$0")
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
echo "$fname [options] <operation>
|
||||||
|
[Global Operations]:
|
||||||
|
list-files List eligible files in data path. Shortcut 'lsf'
|
||||||
|
cache-clear Delete all cached keys. Shortcut 'cc'
|
||||||
|
help Display help
|
||||||
|
rm-file <file...> Remove files. Shortcut 'rmf'
|
||||||
|
[File Operations]:
|
||||||
|
ls [path] List contents at given path
|
||||||
|
tree List all contents
|
||||||
|
create Create a file or change key
|
||||||
|
get <path...> Get values of targets
|
||||||
|
copy <path> Copy the target value to clipboard. Shortcut 'x'
|
||||||
|
set <path> <value> Set the value of target
|
||||||
|
add <path...> Prompt for input value on paths
|
||||||
|
new <path...> Generate a random password at target
|
||||||
|
rm <path...> Delete targets
|
||||||
|
mv <path...> Move targets
|
||||||
|
exec <cmd> Execute the following command inside the archive.
|
||||||
|
cached Returns wether or not a key is currently cached. Shortcut 'ch'
|
||||||
|
rm-cache Delete the cached key for this file. Shortcut 'rmc'
|
||||||
|
|
||||||
|
[Config]:
|
||||||
|
*Variable* *Default value* *Description*
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
CONFIGFILE '\$XDG_CONFIG_HOME/zpass/defaut.conf' Path to the config file to load
|
||||||
|
ZPASS_PATH '\$XDG_DATA_HOME/zpass' Folder containing password files
|
||||||
|
ZPASS_CACHE_PATH '\$XDG_CACHE_HOME/zpass' Path used for caching keys
|
||||||
|
ZPASS_FILE 'default' File to use for operations
|
||||||
|
ZPASS_KEY Key to use for encrypting/decrypting files
|
||||||
|
ZPASS_KEY_CACHE_TIME '60' Time a key stays in cache for decrypting, in seconds
|
||||||
|
ZPASS_CLIPBOARD_TIME '30' Time until clipboard gets cleared after copy, in seconds
|
||||||
|
ZPASS_UNK_OP_CALL 'copy' Operation to call on unrecognized first argument
|
||||||
|
ZPASS_RAND_LEN Length of random passwords generated by 'new'
|
||||||
|
ZPASS_REMOTE_ADDR SSH server the file is on
|
||||||
|
ZPASS_REMOTE_PORT '22' SSH server port
|
||||||
|
ZPASS_SSH_ID SSH private key to use
|
||||||
|
|
||||||
|
All operations can be shortened to their first char unless specified
|
||||||
|
Unknown first argument will perform the operation described in 'ZPASS_UNK_OP_CALL' on that argument
|
||||||
|
"
|
||||||
|
}
|
||||||
34
src/main.sh
Normal file
34
src/main.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/bin/lxsh
|
||||||
|
|
||||||
|
%include util.sh config.sh *.sh
|
||||||
|
|
||||||
|
## pre exec
|
||||||
|
|
||||||
|
clean_cache 2>/dev/null
|
||||||
|
[ $# -lt 1 ] && usage && return 1
|
||||||
|
|
||||||
|
arg=$1
|
||||||
|
shift 1
|
||||||
|
|
||||||
|
## exec
|
||||||
|
|
||||||
|
case $arg in
|
||||||
|
-h|h|help) usage && exit 1;;
|
||||||
|
lsf|list-files) list_files ;;
|
||||||
|
rmf|rm-file) remove_files "$@" ;;
|
||||||
|
cc|cache-clear) clear_cache 2>/dev/null ;;
|
||||||
|
ch|cached) get_key_cached >/dev/null 2>&1 ;;
|
||||||
|
rmc|rm-cache) delete_cache 0 >/dev/null 2>&1 ;;
|
||||||
|
c|create) create ;;
|
||||||
|
t|tree) _tree "$@" ;;
|
||||||
|
s|set) _set "$@" ;;
|
||||||
|
a|add) add "$@" ;;
|
||||||
|
n|new) new "$@" ;;
|
||||||
|
g|get) get "$@" ;;
|
||||||
|
x|copy) copy "$1" ;;
|
||||||
|
e|exec) archive_exec "$@" ;;
|
||||||
|
l|ls|list) sanitize_paths "$@" && __NOPACK=y archive_exec ls -Apw1 -- "$@" ;;
|
||||||
|
r|rm) sanitize_paths "$@" && archive_exec rm -rf -- "$@" ;;
|
||||||
|
m|mv) sanitize_paths "$@" && archive_exec mv -f -- "$@" ;;
|
||||||
|
*) [ -n "$ZPASS_UNK_OP_CALL" ] && "$0" $ZPASS_UNK_OP_CALL "$arg" "$@" ;;
|
||||||
|
esac
|
||||||
75
src/operation.sh
Normal file
75
src/operation.sh
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# $@ = paths
|
||||||
|
_tree()
|
||||||
|
{
|
||||||
|
if [ $# -gt 0 ]
|
||||||
|
then
|
||||||
|
fulltree=$(decrypt | tar -tf - 2>/dev/null) || exit $?;
|
||||||
|
|
||||||
|
for N
|
||||||
|
do
|
||||||
|
[ $# -gt 1 ] && echo "> $N:"
|
||||||
|
echo "$fulltree" | grep "^$(escape_chars "$N")" | sed "s|^$N||g ; "' s|^/||g ; /\/$/d ; /^$/d'
|
||||||
|
done
|
||||||
|
|
||||||
|
else
|
||||||
|
{ decrypt | tar -tf - 2>/dev/null || exit $?; } | sed '/\/$/d ; /^$/d'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $@ = paths
|
||||||
|
get()
|
||||||
|
{
|
||||||
|
[ $# -lt 1 ] && return 1
|
||||||
|
__NOPACK=y archive_exec sh -c '
|
||||||
|
for N
|
||||||
|
do
|
||||||
|
(
|
||||||
|
cat "$N" 2>/dev/null && exit 0
|
||||||
|
[ -d "$1" ] && cat "$N/default" 2>/dev/null && exit 0
|
||||||
|
exit 1
|
||||||
|
) || { echo "$N: not found" >&2 && exit 1; }
|
||||||
|
done
|
||||||
|
' sh "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = path
|
||||||
|
copy()
|
||||||
|
{
|
||||||
|
copy_check || return $?
|
||||||
|
{ get "$1" || return $?; } | remove_trailing_newline | clipboard && clipboard_clear "$ZPASS_CLIPBOARD_TIME"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $@ = path
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
[ $# -lt 1 ] && return 1
|
||||||
|
archive_exec sh -c "
|
||||||
|
for N
|
||||||
|
do
|
||||||
|
mkdir -p \"\$(dirname \"\$N\")\" || exit \$?
|
||||||
|
{ tr -cd 'a-zA-Z0-9!-.' < /dev/urandom | head -c $ZPASS_RAND_LEN && echo; } > \"\$N\" || exit \$?
|
||||||
|
done
|
||||||
|
" sh "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = path , $@ = value
|
||||||
|
_set()
|
||||||
|
{
|
||||||
|
[ $# -lt 1 ] && return 1
|
||||||
|
ref=$1
|
||||||
|
shift 1
|
||||||
|
archive_exec sh -c "mkdir -p '$(dirname "$ref")' && printf '%s\n' '$*' > '$ref'"
|
||||||
|
}
|
||||||
|
|
||||||
|
add()
|
||||||
|
{
|
||||||
|
[ $# -lt 1 ] && return 1
|
||||||
|
archive_exec true # prompt for the key
|
||||||
|
for N
|
||||||
|
do
|
||||||
|
val=$(prompt_password "New value for $N") || return $?
|
||||||
|
_set "$N" "$val" || return $?
|
||||||
|
done
|
||||||
|
}
|
||||||
62
src/prompt.sh
Normal file
62
src/prompt.sh
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# $1 = prompt
|
||||||
|
console_prompt_hidden()
|
||||||
|
{
|
||||||
|
(
|
||||||
|
_tty_on() { stty echo; }
|
||||||
|
trap _tty_on INT
|
||||||
|
local prompt
|
||||||
|
printf "%s" "$1" >&2
|
||||||
|
stty -echo
|
||||||
|
read -r prompt || { stty echo; return 1; }
|
||||||
|
stty echo
|
||||||
|
printf "\n" >&2
|
||||||
|
echo "$prompt"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = prompt message
|
||||||
|
prompt_password() {
|
||||||
|
if [ -n "$DISPLAY" ]
|
||||||
|
then
|
||||||
|
if which kdialog >/dev/null 2>&2
|
||||||
|
then kdialog --title "$fname" --password "$1" 2>/dev/null
|
||||||
|
else zenity --title "$fname" --password 2>/dev/null
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
console_prompt_hidden "$1: "
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = message
|
||||||
|
error_dialog() {
|
||||||
|
if which kdialog >/dev/null 2>&2
|
||||||
|
then kdialog --title "$fname" --error "$1" 2>/dev/null
|
||||||
|
else zenity --title "$fname" --error --text="$1" 2>/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
new_key_with_confirm()
|
||||||
|
{
|
||||||
|
[ -n "$ZPASS_KEY" ] && echo "$ZPASS_KEY" && return 0
|
||||||
|
pass1=1
|
||||||
|
pass2=2
|
||||||
|
while [ "$pass1" != "$pass2" ]
|
||||||
|
do
|
||||||
|
pass1=$(prompt_password "Enter new key") || error 100 "Cancelled"
|
||||||
|
pass2=$(prompt_password "Confirm key") || error 100 "Cancelled"
|
||||||
|
[ "$pass1" != "$pass2" ] && error_dialog "Passwords do not match.\nTry again"
|
||||||
|
done
|
||||||
|
write_cache "$pass1" &
|
||||||
|
echo "$pass1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = prompt message
|
||||||
|
ask_key() {
|
||||||
|
message="Enter key"
|
||||||
|
[ -n "$1" ] && message="$1"
|
||||||
|
key=$(prompt_password "$message") || return $?
|
||||||
|
write_cache "$key" &
|
||||||
|
echo "$key"
|
||||||
|
}
|
||||||
60
src/util.sh
Normal file
60
src/util.sh
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
error(){
|
||||||
|
ret=$1 && shift 1 && echo "$*" >&2 && exit $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
randalnum() {
|
||||||
|
tr -cd '[a-zA-Z]' < /dev/urandom | head -c $1
|
||||||
|
}
|
||||||
|
|
||||||
|
# $* = input
|
||||||
|
escape_chars() {
|
||||||
|
echo "$*" | sed 's|\.|\\\.|g;s|/|\\/|g'
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_trailing_newline() {
|
||||||
|
awk 'NR>1{print PREV} {PREV=$0} END{printf("%s",$0)}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# $@ = paths
|
||||||
|
sanitize_paths()
|
||||||
|
{
|
||||||
|
for N
|
||||||
|
do
|
||||||
|
echo "$N" | grep -q "^/" && echo "Path cannot start with /" >&2 && return 1
|
||||||
|
echo "$N" | grep -qw ".." && echo "Path cannot contain .." >&2 && return 1
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = file
|
||||||
|
getpath() {
|
||||||
|
if [ -n "$ZPASS_REMOTE_ADDR" ]
|
||||||
|
then echo "$ZPASS_REMOTE_PORT:$ZPASS_REMOTE_ADDR:$file"
|
||||||
|
else readlink -f "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = file
|
||||||
|
filehash(){
|
||||||
|
getpath "$file" | md5sum | cut -d' ' -f1
|
||||||
|
}
|
||||||
|
|
||||||
|
keyfile(){
|
||||||
|
printf "%s.key" "$(filehash)"
|
||||||
|
}
|
||||||
|
|
||||||
|
_cmd_() {
|
||||||
|
if [ -n "$ZPASS_REMOTE_ADDR" ]
|
||||||
|
then
|
||||||
|
if [ -n "$ZPASS_SSH_ID" ]
|
||||||
|
then
|
||||||
|
ssh -i "$ZPASS_SSH_ID" "$ZPASS_REMOTE_ADDR" "$@" || return $?
|
||||||
|
else
|
||||||
|
ssh "$ZPASS_REMOTE_ADDR" "$@" || return $?
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
sh -c "$*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue