coffin

secure lan file storage on a device
git clone git://parazyd.org/coffin.git
Log | Files | Refs | Submodules | README | LICENSE

commit b7fbb10958f1c7aab9f940476f93963a9210d579
parent 4bb95404c004caea78e233a8fb4b8ac0b5ba64bb
Author: parazyd <parazyd@dyne.org>
Date:   Mon, 21 Mar 2016 23:45:10 +0100

unfinished refactoring, now using zuper

Diffstat:
A.gitignore | 1+
MREADME.md | 4++--
Dbin/mourner | 23-----------------------
Dbin/sacrist | 219-------------------------------------------------------------------------------
Dgmakehook | 539-------------------------------------------------------------------------------
Ahelpers/gmakehook | 539+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mourner | 23+++++++++++++++++++++++
Asrc/sacrist | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zlibs/features | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/zlibs/hooks | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zlibs/keyfiles | 19+++++++++++++++++++
Asrc/zlibs/mounts | 31+++++++++++++++++++++++++++++++
Asrc/zlibs/ttab | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zlibs/zuper | 729+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zlibs/zuper.init | 35+++++++++++++++++++++++++++++++++++
15 files changed, 1621 insertions(+), 783 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +NOTES.md diff --git a/README.md b/README.md @@ -14,9 +14,9 @@ files are once again unreadable. * `gmakehook` can be used to create tombox hooks in a more user-friendly manner. It is a GUI (zenity) helper script. -## Installation - ## Usage +## Installation + ## Troubleshooting diff --git a/bin/mourner b/bin/mourner @@ -1,23 +0,0 @@ -#!/bin/bash -# -# mourner - inotify script to watch /dev for new keys -# -# ~ parazyd - -pattern='sd[a-z][1-9]$' -coproc inotifywait --monitor --event create,delete --format '%e %w%f' /dev - -while read -r -u "${COPROC[0]}" event file; do - if [[ $file =~ $pattern ]]; then - case $event in - CREATE) - echo "Created $file..."; sleep 1 - `pwd`/sacrist $file $event - ;; - DELETE) - echo "Removed $file..."; sleep 1 - `pwd`/sacrist $file $event - ;; - esac - fi -done diff --git a/bin/sacrist b/bin/sacrist @@ -1,219 +0,0 @@ -#!/bin/zsh -# -# sacrist - script called by mourner, for our graveyard administration -# -# ~ parazyd - -device=$1 -happenz=$2 -keyuuid=$(blkid $device | awk -F\" '{print $2}') - -typeset -H keypass -typeset -H keyuuid -typeset -H undertaker -typeset -H graveyard -typeset -H tombs -typeset -H tombpasswd - -# Vars -graveyard="/home/graveyard" # Our graveyard, with all the tombs -tombs="$graveyard/tombs" # Info about opened tombs, holds keyuuid, keyhash and tombid -tmptombs="$graveyard/tmptombs" # Temp tempfile, for updating $tombs -keymount="/media/tombkey" # Directory where keys get mounted -coffindot="$keymount/.coffin" # .coffin directory on the usb key -ttab="$coffindot/ttab" # Our ttab -hooks="$coffindot/hook" -tomb="/usr/local/bin/tomb" -tombpasswd="$graveyard/passwd" - -# Debugs -print "Arg1: $1" -print "Arg2: $2" -print "Device path is: $device" -print "Device UUID is: $keyuuid" - -# {{{ msg -_msg() { - if [[ $1 == "error" ]]; then - print "\e[1;31m[E] \e[0;31m$2 \e[0m" - elif [[ $1 == "warn" ]]; then - print "\e[1;33m[W] \e[0;33m$2 \e[0m" - elif [[ $1 == "info" ]]; then - print "\e[1;34m[i] \e[0;34m$2 \e[0m" - fi -} -# }}} - -_umountkey() { umount $keymount; rmdir $keymount } -_mountkey() { mkdir -p $keymount; mount $device $keymount } - -_hashkey() { cat $coffindot/$tombid.key | sha512sum | awk -F" " '{print $1}' } - -_comparekey() { - keyhash=$(_hashkey) - if [[ ( $(cat $tombs | grep $keyhash | grep $keyuuid) ) ]]; then - happenz=close - else - happenz=open - fi -} - -_hooks() { - for entry in $(cat $hooks); do - let hook=$hook+1 - _msg info "Found hook $hook..." - - # Check what's hook supposed to do - if [[ ${entry[(ws@:@)1]} == "create" ]]; then - _create_new_tomb - elif [[ ${entry[(ws@:@)1]} == "delete" ]]; then - #DELETE TOMB - elif [[ ${entry[(ws@:@)1]} == "backup" ]]; then - # do backup - else - _msg error "No valid hook syntax on hook $hook" - fi - rm $hooks && _msg info "Removed $hooks" - done -} - -# {{{ ttabmagic -_ttabmagic() { - # Loop entire ttab and do stuff for tombs that want to be opened - _msg info "Doing ttab magic..." - line=0 - - for entry in $(cat $ttab); do - let line=$line+1 - _msg info "Found line $line..." - - if [[ ${entry[(ws@:@)3]} == "true" ]]; then - _msg info "Working on tomb from line $line..." - undertaker=${entry[(ws@:@)1]} - print "Username: $undertaker" - tombid=${entry[(ws@:@)2]} - print "Tomb name: $tombid.tomb" - _comparekey - - if [[ $happenz == "close" ]]; then - _msg warn "Comparekey true" - sudo -u $undertaker $tomb slam $tombid - cp $tombs $tmptombs - grep -v $keyhash $tmptombs > $tombs && chmod 600 $tombs && _msg info "Updated $tombs" - rm $tmptombs - continue - fi - - _msg warn "Comparekey false" - keypass=$(cat $tombpasswd | grep $keyhash | awk -F: '{print $2}') - print "Tomb passphrase: $keypass" - sudo -u $undertaker $tomb open $graveyard/$tombid.tomb -k $coffindot/$tombid.key \ - --unsafe --tomb-pwd "$keypass" - - chmod g+rw /media/$tombid - if [[ -d "/media/$tombid" ]]; then - print "$undertaker:$keyhash:$keyuuid" >> $tombs && chmod 600 $tombs && _msg info "Added info to $tombs" - else - _msg warn "Nothing added to $tombs" - fi - fi - - done - umount $keymount && rmdir $keymount && _msg info "Unmounted and deleted $keymount" -} -# }}} - -# {{{ _create_new_tomb -_create_new_tomb() { - # TODO: options for webdav, sshfs, ipfs - # TODO: recognize custom post/bind hooks and implement them inside the new tomb - # TODO: delete/backup/foo hooks - - _msg info "Creating new tomb!" - undertaker=${entry[(ws@:@)2]} - tombid=${entry[(ws@:@)3]} - tombsize=${entry[(ws@:@)4]} - keypass=$(pwgen 30 -1 1) - - if ! [[ ( $(id $undertaker) ) ]]; then - _msg warn "No user called $undertaker found. Creating..." - useradd -G tombox -m -s /bin/bash $undertaker - _msg info "Created user $undertaker" - else - _msg warn "User $undertaker exists. Continuing..." - fi - - if [[ $entry =~ webdav && -f $coffindot/webdav.conf ]]; then - _msg info "Found WebDAV data. Setting up..." - if [[ -f $coffindot/davinfo ]]; then - cat $coffindot/davinfo >> /etc/apache2/davpasswd - rm $coffindot/davinfo - gpasswd -a www-data $undertaker - _msg info "Wrote to davpasswd" - fi - sed -i -e :a -e '$d;N;2,3ba' -e 'P;D' /etc/apache2/sites-available/tomboxdav.conf - cat $coffindot/webdav.conf >> /etc/apache2/sites-available/tomboxdav.conf - _msg info "Wrote to tomboxdav.conf" - rm $coffindot/webdav.conf - /etc/init.d/apache2 restart - _msg info "Done setting up WebDAV!" - else - _msg info "No WebDAV data found. Continuing..." - fi - - if [[ $entry =~ sshfs && -f $coffindot/sshpubkey ]]; then - _msg info "Found SSH info. Setting up..." - mkdir -p /home/$undertaker/.ssh - cat $coffindot/sshpubkey >> /home/$undertaker/.ssh/authorized_keys - chown -R $undertaker:$undertaker /home/$undertaker/.ssh - _msg info "Wrote to authorized_keys" - rm $coffindot/sshpubkey - _msg info "Done setting up SSH." - else - _msg info "No SSH data found. Continuing..." - fi - - sudo -u $undertaker $tomb dig -s $tombsize $graveyard/$tombid.tomb - sudo -u $undertaker $tomb forge $graveyard/$tombid.key --unsafe --tomb-pwd "$keypass" - sudo -u $undertaker $tomb lock $graveyard/$tombid.tomb -k $graveyard/$tombid.key \ - --unsafe --tomb-pwd "$keypass" - - mv $graveyard/$tombid.key $coffindot/ && chown $undertaker:$undertaker $coffindot/$tombid.key && \ - _msg info "Moved and chowned keyfile" - print "${undertaker}:${tombid}:true" >> $ttab - keyhash=$(_hashkey) - print "${keyhash}:${keypass}" >> $tombpasswd - _msg info "Wrote to $ttab and $tombpasswd" - # rm $createme && _msg info "Removed $createme" -} -# }}} - -_endgame() { - # Mr. Proper -} - - -# {{{ MAIN -if [[ $happenz == "CREATE" ]]; then - _mountkey - if [[ -d "$coffindot" ]]; then - _msg info "$coffindot found..." - - if [[ -f "$hooks" ]]; then - _hooks - else - _msg warn "No hooks detected" - fi - - if ! [[ -f "$ttab" ]]; then - _msg error "No ttab!" - umount $keymount && rmdir $keymount && _msg info "Unmounted and removed $keymount" - else - _ttabmagic - fi - else - _msg error "No valid .coffin directory! Exiting..." - _umountkey && _msg info "Unmounted and removed $keymount" - fi -fi -# }}} diff --git a/gmakehook b/gmakehook @@ -1,539 +0,0 @@ -#!/usr/bin/env zsh -# -# Zenity helper script to make tombox hooks more user-friendly -# -# ~ parazyd - -typeset -H UNDERTAKER -typeset -H TOMBSIZE -typeset -H TOMBNAME -typeset -H sudoassword - -keymount="/media/tombkey" -coffindot="$keymount/.coffin" -hooks="$coffindot/hook" - -# {{{ icon -MONMORT="/tmp/monmort.png" -ICONB64="iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAAmJLR0T//xSrMc0AAAAJcEhZcwAA -AEgAAABIAEbJaz4AAAAJdnBBZwAAACAAAAAgAIf6nJ0AAADrSURBVFjD5ZZBEsMgCEU/TO/l2XMx -04VjQ1K1CDYswkwWJnH+E/EL4RP7jluDCACoim/bvfIpFQiKEJcQHCN9xEtLCgDMQM7f33sZrPNG -/05loCXujfAtCAVgNgLwIuycjQAra8G9Fm823ADabPRA1QDelfZAVUZktWrNvL8ew5YTnsStx3Am -AyOInJVbYF1prZuU+tsR1g9UMDqGuo5oFWhtSEQNEGmeVrqv73Tj0pIZirANMYqRhyw5Bb9MauSW -SwA8l9OzG5LnAsiiDQGQRRvaEwInK54J390hndAIYIeQ4k6AAjE/h06ab0SjP08MA1xDAAAAJXRF -WHRkYXRlOmNyZWF0ZQAyMDExLTAxLTEyVDA5OjM0OjI0KzAxOjAwo//d7wAAACV0RVh0ZGF0ZTpt -b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII=" -print "$ICONB64" | base64 --decode > $MONMORT -# }}} - -# {{{ sudo functions -function _sudo { - sudoassword=$(ask_password "Insert sudo password for user $USER") - print "$sudoassword\n" | sudo -S -v - _sudowrong -} -function _sudowrong { - if [[ $? == 1 ]]; then - sudoassword=$(ask_password "Wrong password. Insert sudo password for user $USER") - print "$sudoassword\n" | sudo -S -v - _sudowrong - fi -} -# }}} - -# {{{ Some pinentry code shamelessly stolen from tomb -# Ask user for a password -# Wraps around the pinentry command, from the GnuPG project, as it -# provides better security and conveniently use the right toolkit. -ask_password() { - local description="$1" - local title="${2:-Enter tomb password.}" - local output - local password - local gtkrc - local theme - - # Distributions have broken wrappers for pinentry: they do - # implement fallback, but they disrupt the output somehow. We are - # better off relying on less intermediaries, so we implement our - # own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4 - # and x11. - - # make sure LANG is set, default to C - LANG=${LANG:-C} - - - if [[ "$DISPLAY" = "" ]]; then - - if _is_found "pinentry-curses"; then - output=`cat <<EOF | pinentry-curses -OPTION ttyname=$TTY -OPTION lc-ctype=$LANG -SETTITLE $title -SETDESC $description -SETPROMPT Password: -GETPIN -EOF` - fi - - else # a DISPLAY is found to be active - - # customized gtk2 dialog with a skull (if extras are installed) - if _is_found "pinentry-gtk-2"; then - - gtkrc="" - theme=/share/themes/tomb/gtk-2.0-key/gtkrc - for i in /usr/local /usr; do - [[ -r $i/$theme ]] && { - gtkrc="$i/$theme" - break - } - done - [[ "$gtkrc" = "" ]] || { - gtkrc_old="$GTK2_RC_FILES" - export GTK2_RC_FILES="$gtkrc" - } - output=`cat <<EOF | pinentry-gtk-2 -OPTION ttyname=$TTY -OPTION lc-ctype=$LANG -SETTITLE $title -SETDESC $description -SETPROMPT Password: -GETPIN -EOF` - [[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old" - - # TODO QT4 customization of dialog - elif _is_found "pinentry-qt4"; then - - # TODO X11 customization of dialog - elif _is_found "pinentry-x11"; then - - output=`cat <<EOF | pinentry-x11 -OPTION ttyname=$TTY -OPTION lc-ctype=$LANG -SETTITLE $title -SETDESC $description -SETPROMPT Password: -GETPIN -EOF` - - else - - if _is_found "pinentry-curses"; then - - output=`cat <<EOF | pinentry-curses -OPTION ttyname=$TTY -OPTION lc-ctype=$LANG -SETTITLE $title -SETDESC $description -SETPROMPT Password: -GETPIN -EOF` - else - fi - - fi - - fi # end of DISPLAY block - - # parse the pinentry output - for i in ${(f)output}; do - [[ "$i" =~ "^ERR.*" ]] && { - print "canceled" - return 1 } - - # here the password is found - [[ "$i" =~ "^D .*" ]] && password="${i##D }" - done - - [[ "$password" = "" ]] && { - print "empty" - return 1 } - - print "$password" - return 0 -} - -_is_found() { - # returns 0 if binary is found in path - [[ "$1" = "" ]] && return 1 - command -v "$1" 1>/dev/null 2>/dev/null - return $? -} -# }}} - -# {{{ _clean - Clean function, removes sensitive stuff from memory -function _clean { - TOMBSIZE="$rr"; unset TOMBSIZE - TOMBNAME="$rr"; unset TOMBNAME - UNDERTAKER="$rr"; unset UNDERTAKER - sudoassword="$rr"; unset sudoassword -} -# }}} - -# {{{ zenity dialogs -function _fsel { - zenity \ - --window-icon="$MONMORT" \ - --file-selection \ - --title="$1" -} -function _zenques { - zenity \ - --window-icon="$MONMORT" \ - --question \ - --text="$1" -} -function _zenwarn { - zenity \ - --window-icon="$MONMORT" \ - --warning \ - --title="$1" \ - --text="$2" -} -function _zeninfo { - zenity \ - --window-icon="$MONMORT" \ - --info \ - --title="$1" \ - --text="$2" -} -function _zenerr { - zenity \ - --window-icon="$MONMORT" \ - --error \ - --title="$1" \ - --text="$2" -} -function _zenentry { - zenity \ - --window-icon="$MONMORT" \ - --entry \ - --title="$1" \ - --text="$2" \ - --entry-text="$3" -} -# }}} - -function _umountkey { sudo umount $keymount && sudo rmdir $keymount } -function _mountkey { - sudo umount $USBKEY - if [[ $? = 1 ]]; then - _zenerr "Error" "Your key is mounted somewhere and I've run into issues. Please unmount it and re-run." - _clean && exit - fi - if [[ -d $keymount ]]; then - _zenwarn "Warning" "Something is already mounted on $keymount. Please check and re-run." - _clean && exit - fi - - sudo mkdir -p $keymount - sudo mount $USBKEY $keymount - - if ! [[ -d $coffindot ]]; then - sudo mkdir $coffindot - fi -} - -# {{{ _writedavinfo -function _writedavinfo { - if [[ -n $davpass ]]; then - davinfo=$(echo -n "$UNDERTAKER:WebDAV:" \ - && echo -n "$UNDERTAKER:WebDAV:$davpass" \ - | md5sum \ - | awk '{print $1}') - print $davinfo | sudo tee $coffindot/davinfo - fi - - cat <<EOF | sudo tee $coffindot/webdav.conf - - alias /${TOMBNAME} /media/${TOMBNAME} - <Directory "/media/${TOMBNAME}"> - Dav On - AllowOverride none - Options Indexes FollowSymlinks - AuthType Digest - AuthName WebDAV - AuthUserFile /etc/apache2/davpasswd - Require user ${UNDERTAKER} - </Directory> - - </VirtualHost> -</IfModule> -EOF -} -# }}} - -function _main { - _clean - cmnd=$(zenity \ - --window-icon="$MONMORT" \ - --title="tombox hook helper" \ - --list \ - --hide-header \ - --text="tombox hook helper\nChoose what hook you want to create:" \ - --separator=" # " \ - --column=Function \ - --column=Description \ - "create" "a new tomb on the tombox" \ - "delete" "a tomb from the tombox" \ - "backup" "a tomb from the tombox" \ - "foobar" "on the tombox") - eval "_$cmnd" -} - -# {{{ _create -function _create { - vars=$(zenity \ - --window-icon="$MONMORT" \ - --title="Create a new tomb" \ - --forms \ - --text="Enter the info for your new tomb" \ - --separator=":" \ - --add-entry="Tombox username" \ - --add-entry="Tomb name" \ - --add-entry="Tomb's size in MiB") - if [[ $? = 1 ]]; then - exec _main - else - UNDERTAKER=${vars[(ws@:@)1]} - re='^[A-Za-z0-9]+$' - if ! [[ $UNDERTAKER =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in username!" - exec _create - fi - TOMBNAME=${vars[(ws@:@)2]} - if ! [[ $TOMBNAME =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in tomb name!" - exec _create - fi - TOMBSIZE=${vars[(ws@:@)3]} - re='^[0-9]+$' - if ! [[ $TOMBSIZE =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in tomb size!" - exec _create - fi - fi - - feats=$(zenity \ - --window-icon="$MONMORT" \ - --title="Choose features" \ - --text="Choose features you wish to include with your tomb" \ - --list \ - --checklist \ - --separator=":" \ - --column=Yes/No \ - --column=Feature \ - "FALSE" "webdav" \ - "FALSE" "sshfs" \ - "FALSE" "ipfs") - if [[ $? = 1 ]]; then; exec _main; fi - - if [[ $feats =~ "webdav" ]]; then - _zenques "Do you already have a WebDAV login?" - if [[ $? = 1 ]]; then - davpass=$(_zenentry "WebDAV Password" \ - "Insert the password you want to use for WebDAV login:" \ - "my very strong password") - if [[ $? = 1 ]]; then; davpass=""; fi - fi - fi - - if [[ $feats =~ "sshfs" ]]; then - _zenques "Do you already have an SSH key setup for your user?" - if [[ $? = 1 ]]; then - sshpubkey=$(_fsel "Select your SSH pubkey you want to use.") - else - sshpubkey="" - fi - fi - - TOMBHOOKS=$(_zenques "Choose 'Yes' if you want to edit your tomb's bind-hooks and post-hooks") - if [[ $? = 0 ]]; then - bindhook=$(zenity \ - --window-icon="$MONMORT" \ - --title="bind-hooks" \ - --checkbox="Accept these bind-hooks" \ - --text-info \ - --editable) - if [[ $? = 1 ]]; then; exec _main; fi - - posthook=$(zenity \ - --window-icon="$MONMORT" \ - --title="post-hooks" \ - --checkbox="Accept these post-hooks" \ - --text-info \ - --editable) - if [[ $? = 1 ]]; then; exec _main; fi - fi - - _zeninfo "gmakehook" "Plug in your USB key and click OK." - lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs - - USBKEY=$(zenity \ - --window-icon="$MONMORT" \ - --title="Choose USB key" \ - --list \ - --text="Choose your USB key to use" \ - --separator=" " \ - --column="Device path" \ - --column="Device size" \ - $(cat ./.devs)) - if [[ $? = 1 ]]; then; exec _main; fi - - _sudo - _mountkey - if [[ -f $hooks ]]; then - _zenques "Warning! There is already an existing hook. Do you want to overwrite?" - if [[ $? = 1 ]]; then - _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main - else - print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks - if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi - if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey ./sshpubkey ; fi - if [[ -n $bindhook ]]; then - print "$bindhook" | sudo tee $coffindot/bindhooks - fi - if [[ -n $posthook ]]; then - print "$posthook" | sudo tee $coffindot/posthooks - fi - _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it." - exec _main - fi - else - print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks - if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi - if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey $coffindot/sshpubkey ; fi - if [[ -n $bindhook ]]; then - print "$bindhook" | sudo tee $coffindot/bindhooks - fi - if [[ -n $posthook ]]; then - print "$posthook" | sudo tee $coffindot/posthooks - fi - _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it." - exec _main - fi -} -# }}} - -# {{{ _delete -function _delete { - vars=$(zenity \ - --window-icon="$MONMORT" \ - --title="Delete an existing tomb" \ - --forms \ - --text="Enter the info of your tomb" \ - --separator=":" \ - --add-entry="Tombox username" \ - --add-entry="Tomb name") - if [[ $? = 1 ]]; then - exec _main - else - UNDERTAKER=${vars[(ws@:@)1]} - re='^[A-Za-z0-9]+$' - if ! [[ $UNDERTAKER =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in username!" - exec _delete - fi - TOMBNAME=${vars[(ws@:@)2]} - if ! [[ $TOMBNAME =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in tomb name!" - exec _delete - fi - fi - - _zeninfo "gmakehook" "Plug in your USB key and click OK." - lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs - - USBKEY=$(zenity \ - --window-icon="$MONMORT" \ - --title="Choose USB key" \ - --list \ - --text="Choose your USB key to use" \ - --separator=" " \ - --column="Device path" \ - --column="Device size" \ - $(cat ./.devs)) - if [[ $? = 1 ]]; then; exec _main; fi - - _sudo - _mountkey - if [[ -f $hooks ]]; then - _zenques "Warning! There is already an existing hook. Do you want to overwrite?" - if [[ $? = 1 ]]; then - _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main - else - print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks - _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it." - exec _main - fi - else - print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks - _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it." - exec _main - fi -} -# }}} - -# {{{ _backup -function _backup { - vars=$(zenity \ - --window-icon="$MONMORT" \ - --title="Backup an existing tomb" \ - --forms \ - --text="Enter the info of your tomb" \ - --separator=":" \ - --add-entry="Tombox username" \ - --add-entry="Tomb name") - if [[ $? = 1 ]]; then - exec _main - else - UNDERTAKER=${vars[(ws@:@)1]} - re='^[A-Za-z0-9]+$' - if ! [[ $UNDERTAKER =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in username!" - exec _delete - fi - TOMBNAME=${vars[(ws@:@)2]} - if ! [[ $TOMBNAME =~ $re ]]; then - _zenwarn "Warning" "Invalid characters in tomb name!" - exec _delete - fi - fi - - _zeninfo "gmakehook" "Plug in your USB key and click OK." - lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs - - USBKEY=$(zenity \ - --window-icon="$MONMORT" \ - --title="Choose USB key" \ - --list \ - --text="Choose your USB key to use" \ - --separator=" " \ - --column="Device path" \ - --column="Device size" \ - $(cat ./.devs)) - if [[ $? = 1 ]]; then; exec _main; fi - - _sudo - _mountkey - if [[ -f $hooks ]]; then - _zenques "Warning! There is already an existing hook. Do you want to overwrite?" - if [[ $? = 1 ]]; then - _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main - else - print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks - _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it." - exec _main - fi - else - print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks - _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it." - exec _main - fi -} -# }}} - -function _ { rm ./.devs } -exec _main diff --git a/helpers/gmakehook b/helpers/gmakehook @@ -0,0 +1,539 @@ +#!/usr/bin/env zsh +# +# Zenity helper script to make tombox hooks more user-friendly +# +# ~ parazyd + +typeset -H UNDERTAKER +typeset -H TOMBSIZE +typeset -H TOMBNAME +typeset -H sudoassword + +keymount="/media/tombkey" +coffindot="$keymount/.coffin" +hooks="$coffindot/hook" + +# {{{ icon +MONMORT="/tmp/monmort.png" +ICONB64="iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAAmJLR0T//xSrMc0AAAAJcEhZcwAA +AEgAAABIAEbJaz4AAAAJdnBBZwAAACAAAAAgAIf6nJ0AAADrSURBVFjD5ZZBEsMgCEU/TO/l2XMx +04VjQ1K1CDYswkwWJnH+E/EL4RP7jluDCACoim/bvfIpFQiKEJcQHCN9xEtLCgDMQM7f33sZrPNG +/05loCXujfAtCAVgNgLwIuycjQAra8G9Fm823ADabPRA1QDelfZAVUZktWrNvL8ew5YTnsStx3Am +AyOInJVbYF1prZuU+tsR1g9UMDqGuo5oFWhtSEQNEGmeVrqv73Tj0pIZirANMYqRhyw5Bb9MauSW +SwA8l9OzG5LnAsiiDQGQRRvaEwInK54J390hndAIYIeQ4k6AAjE/h06ab0SjP08MA1xDAAAAJXRF +WHRkYXRlOmNyZWF0ZQAyMDExLTAxLTEyVDA5OjM0OjI0KzAxOjAwo//d7wAAACV0RVh0ZGF0ZTpt +b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII=" +print "$ICONB64" | base64 --decode > $MONMORT +# }}} + +# {{{ sudo functions +function _sudo { + sudoassword=$(ask_password "Insert sudo password for user $USER") + print "$sudoassword\n" | sudo -S -v + _sudowrong +} +function _sudowrong { + if [[ $? == 1 ]]; then + sudoassword=$(ask_password "Wrong password. Insert sudo password for user $USER") + print "$sudoassword\n" | sudo -S -v + _sudowrong + fi +} +# }}} + +# {{{ Some pinentry code shamelessly stolen from tomb +# Ask user for a password +# Wraps around the pinentry command, from the GnuPG project, as it +# provides better security and conveniently use the right toolkit. +ask_password() { + local description="$1" + local title="${2:-Enter tomb password.}" + local output + local password + local gtkrc + local theme + + # Distributions have broken wrappers for pinentry: they do + # implement fallback, but they disrupt the output somehow. We are + # better off relying on less intermediaries, so we implement our + # own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4 + # and x11. + + # make sure LANG is set, default to C + LANG=${LANG:-C} + + + if [[ "$DISPLAY" = "" ]]; then + + if _is_found "pinentry-curses"; then + output=`cat <<EOF | pinentry-curses +OPTION ttyname=$TTY +OPTION lc-ctype=$LANG +SETTITLE $title +SETDESC $description +SETPROMPT Password: +GETPIN +EOF` + fi + + else # a DISPLAY is found to be active + + # customized gtk2 dialog with a skull (if extras are installed) + if _is_found "pinentry-gtk-2"; then + + gtkrc="" + theme=/share/themes/tomb/gtk-2.0-key/gtkrc + for i in /usr/local /usr; do + [[ -r $i/$theme ]] && { + gtkrc="$i/$theme" + break + } + done + [[ "$gtkrc" = "" ]] || { + gtkrc_old="$GTK2_RC_FILES" + export GTK2_RC_FILES="$gtkrc" + } + output=`cat <<EOF | pinentry-gtk-2 +OPTION ttyname=$TTY +OPTION lc-ctype=$LANG +SETTITLE $title +SETDESC $description +SETPROMPT Password: +GETPIN +EOF` + [[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old" + + # TODO QT4 customization of dialog + elif _is_found "pinentry-qt4"; then + + # TODO X11 customization of dialog + elif _is_found "pinentry-x11"; then + + output=`cat <<EOF | pinentry-x11 +OPTION ttyname=$TTY +OPTION lc-ctype=$LANG +SETTITLE $title +SETDESC $description +SETPROMPT Password: +GETPIN +EOF` + + else + + if _is_found "pinentry-curses"; then + + output=`cat <<EOF | pinentry-curses +OPTION ttyname=$TTY +OPTION lc-ctype=$LANG +SETTITLE $title +SETDESC $description +SETPROMPT Password: +GETPIN +EOF` + else + fi + + fi + + fi # end of DISPLAY block + + # parse the pinentry output + for i in ${(f)output}; do + [[ "$i" =~ "^ERR.*" ]] && { + print "canceled" + return 1 } + + # here the password is found + [[ "$i" =~ "^D .*" ]] && password="${i##D }" + done + + [[ "$password" = "" ]] && { + print "empty" + return 1 } + + print "$password" + return 0 +} + +_is_found() { + # returns 0 if binary is found in path + [[ "$1" = "" ]] && return 1 + command -v "$1" 1>/dev/null 2>/dev/null + return $? +} +# }}} + +# {{{ _clean - Clean function, removes sensitive stuff from memory +function _clean { + TOMBSIZE="$rr"; unset TOMBSIZE + TOMBNAME="$rr"; unset TOMBNAME + UNDERTAKER="$rr"; unset UNDERTAKER + sudoassword="$rr"; unset sudoassword +} +# }}} + +# {{{ zenity dialogs +function _fsel { + zenity \ + --window-icon="$MONMORT" \ + --file-selection \ + --title="$1" +} +function _zenques { + zenity \ + --window-icon="$MONMORT" \ + --question \ + --text="$1" +} +function _zenwarn { + zenity \ + --window-icon="$MONMORT" \ + --warning \ + --title="$1" \ + --text="$2" +} +function _zeninfo { + zenity \ + --window-icon="$MONMORT" \ + --info \ + --title="$1" \ + --text="$2" +} +function _zenerr { + zenity \ + --window-icon="$MONMORT" \ + --error \ + --title="$1" \ + --text="$2" +} +function _zenentry { + zenity \ + --window-icon="$MONMORT" \ + --entry \ + --title="$1" \ + --text="$2" \ + --entry-text="$3" +} +# }}} + +function _umountkey { sudo umount $keymount && sudo rmdir $keymount } +function _mountkey { + sudo umount $USBKEY + [[ $? = 0 ]] || { + _zenerr "Error" "Your key is mounted somewhere and I've run into issues. Please unmount it and re-run." + _clean && exit } + + if [[ -d $keymount ]]; then + _zenwarn "Warning" "Something is already mounted on $keymount. Please check and re-run." + _clean && exit + fi + + sudo mkdir -p $keymount + sudo mount $USBKEY $keymount + + if ! [[ -d $coffindot ]]; then + sudo mkdir $coffindot + fi +} + +# {{{ _writedavinfo +function _writedavinfo { + if [[ -n $davpass ]]; then + davinfo=$(echo -n "$UNDERTAKER:WebDAV:" \ + && echo -n "$UNDERTAKER:WebDAV:$davpass" \ + | md5sum \ + | awk '{print $1}') + print $davinfo | sudo tee $coffindot/davinfo + fi + + cat <<EOF | sudo tee $coffindot/webdav.conf + + alias /${TOMBNAME} /media/${TOMBNAME} + <Directory "/media/${TOMBNAME}"> + Dav On + AllowOverride none + Options Indexes FollowSymlinks + AuthType Digest + AuthName WebDAV + AuthUserFile /etc/apache2/davpasswd + Require user ${UNDERTAKER} + </Directory> + + </VirtualHost> +</IfModule> +EOF +} +# }}} + +function _main { + _clean + cmnd=$(zenity \ + --window-icon="$MONMORT" \ + --title="tombox hook helper" \ + --list \ + --hide-header \ + --text="tombox hook helper\nChoose what hook you want to create:" \ + --separator=" # " \ + --column=Function \ + --column=Description \ + "create" "a new tomb on the tombox" \ + "delete" "a tomb from the tombox" \ + "backup" "a tomb from the tombox" \ + "foobar" "on the tombox") + eval "_$cmnd" +} + +# {{{ _create +function _create { + vars=$(zenity \ + --window-icon="$MONMORT" \ + --title="Create a new tomb" \ + --forms \ + --text="Enter the info for your new tomb" \ + --separator=":" \ + --add-entry="Tombox username" \ + --add-entry="Tomb name" \ + --add-entry="Tomb's size in MiB") + if [[ $? = 1 ]]; then + exec _main + else + UNDERTAKER=${vars[(ws@:@)1]} + re='^[A-Za-z0-9]+$' + if ! [[ $UNDERTAKER =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in username!" + exec _create + fi + TOMBNAME=${vars[(ws@:@)2]} + if ! [[ $TOMBNAME =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in tomb name!" + exec _create + fi + TOMBSIZE=${vars[(ws@:@)3]} + re='^[0-9]+$' + if ! [[ $TOMBSIZE =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in tomb size!" + exec _create + fi + fi + + feats=$(zenity \ + --window-icon="$MONMORT" \ + --title="Choose features" \ + --text="Choose features you wish to include with your tomb" \ + --list \ + --checklist \ + --separator=":" \ + --column=Yes/No \ + --column=Feature \ + "FALSE" "webdav" \ + "FALSE" "sshfs" \ + "FALSE" "ipfs") + if [[ $? = 1 ]]; then; exec _main; fi + + if [[ $feats =~ "webdav" ]]; then + _zenques "Do you already have a WebDAV login?" + if [[ $? = 1 ]]; then + davpass=$(_zenentry "WebDAV Password" \ + "Insert the password you want to use for WebDAV login:" \ + "my very strong password") + if [[ $? = 1 ]]; then; davpass=""; fi + fi + fi + + if [[ $feats =~ "sshfs" ]]; then + _zenques "Do you already have an SSH key setup for your user?" + if [[ $? = 1 ]]; then + sshpubkey=$(_fsel "Select your SSH pubkey you want to use.") + else + sshpubkey="" + fi + fi + + TOMBHOOKS=$(_zenques "Choose 'Yes' if you want to edit your tomb's bind-hooks and post-hooks") + if [[ $? = 0 ]]; then + bindhook=$(zenity \ + --window-icon="$MONMORT" \ + --title="bind-hooks" \ + --checkbox="Accept these bind-hooks" \ + --text-info \ + --editable) + if [[ $? = 1 ]]; then; exec _main; fi + + posthook=$(zenity \ + --window-icon="$MONMORT" \ + --title="post-hooks" \ + --checkbox="Accept these post-hooks" \ + --text-info \ + --editable) + if [[ $? = 1 ]]; then; exec _main; fi + fi + + _zeninfo "gmakehook" "Plug in your USB key and click OK." + lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs + + USBKEY=$(zenity \ + --window-icon="$MONMORT" \ + --title="Choose USB key" \ + --list \ + --text="Choose your USB key to use" \ + --separator=" " \ + --column="Device path" \ + --column="Device size" \ + $(cat ./.devs)) + if [[ $? = 1 ]]; then; exec _main; fi + + _sudo + _mountkey + if [[ -f $hooks ]]; then + _zenques "Warning! There is already an existing hook. Do you want to overwrite?" + if [[ $? = 1 ]]; then + _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main + else + print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks + if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi + if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey ./sshpubkey ; fi + if [[ -n $bindhook ]]; then + print "$bindhook" | sudo tee $coffindot/bindhooks + fi + if [[ -n $posthook ]]; then + print "$posthook" | sudo tee $coffindot/posthooks + fi + _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it." + exec _main + fi + else + print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks + if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi + if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey $coffindot/sshpubkey ; fi + if [[ -n $bindhook ]]; then + print "$bindhook" | sudo tee $coffindot/bindhooks + fi + if [[ -n $posthook ]]; then + print "$posthook" | sudo tee $coffindot/posthooks + fi + _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it." + exec _main + fi +} +# }}} + +# {{{ _delete +function _delete { + vars=$(zenity \ + --window-icon="$MONMORT" \ + --title="Delete an existing tomb" \ + --forms \ + --text="Enter the info of your tomb" \ + --separator=":" \ + --add-entry="Tombox username" \ + --add-entry="Tomb name") + if [[ $? = 1 ]]; then + exec _main + else + UNDERTAKER=${vars[(ws@:@)1]} + re='^[A-Za-z0-9]+$' + if ! [[ $UNDERTAKER =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in username!" + exec _delete + fi + TOMBNAME=${vars[(ws@:@)2]} + if ! [[ $TOMBNAME =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in tomb name!" + exec _delete + fi + fi + + _zeninfo "gmakehook" "Plug in your USB key and click OK." + lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs + + USBKEY=$(zenity \ + --window-icon="$MONMORT" \ + --title="Choose USB key" \ + --list \ + --text="Choose your USB key to use" \ + --separator=" " \ + --column="Device path" \ + --column="Device size" \ + $(cat ./.devs)) + if [[ $? = 1 ]]; then; exec _main; fi + + _sudo + _mountkey + if [[ -f $hooks ]]; then + _zenques "Warning! There is already an existing hook. Do you want to overwrite?" + if [[ $? = 1 ]]; then + _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main + else + print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks + _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it." + exec _main + fi + else + print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks + _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it." + exec _main + fi +} +# }}} + +# {{{ _backup +function _backup { + vars=$(zenity \ + --window-icon="$MONMORT" \ + --title="Backup an existing tomb" \ + --forms \ + --text="Enter the info of your tomb" \ + --separator=":" \ + --add-entry="Tombox username" \ + --add-entry="Tomb name") + if [[ $? = 1 ]]; then + exec _main + else + UNDERTAKER=${vars[(ws@:@)1]} + re='^[A-Za-z0-9]+$' + if ! [[ $UNDERTAKER =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in username!" + exec _delete + fi + TOMBNAME=${vars[(ws@:@)2]} + if ! [[ $TOMBNAME =~ $re ]]; then + _zenwarn "Warning" "Invalid characters in tomb name!" + exec _delete + fi + fi + + _zeninfo "gmakehook" "Plug in your USB key and click OK." + lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs + + USBKEY=$(zenity \ + --window-icon="$MONMORT" \ + --title="Choose USB key" \ + --list \ + --text="Choose your USB key to use" \ + --separator=" " \ + --column="Device path" \ + --column="Device size" \ + $(cat ./.devs)) + if [[ $? = 1 ]]; then; exec _main; fi + + _sudo + _mountkey + if [[ -f $hooks ]]; then + _zenques "Warning! There is already an existing hook. Do you want to overwrite?" + if [[ $? = 1 ]]; then + _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main + else + print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks + _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it." + exec _main + fi + else + print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks + _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it." + exec _main + fi +} +# }}} + +function _ { rm ./.devs } +exec _main diff --git a/src/mourner b/src/mourner @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# mourner - inotify script to watch /dev for new keys +# +# ~ parazyd + +pattern='sd[a-z][1-9]$' +coproc inotifywait --monitor --event create,delete --format '%e %w%f' /dev + +while read -r -u "${COPROC[0]}" event file; do + if [[ $file =~ $pattern ]]; then + case $event in + CREATE) + echo "Created $file..."; sleep 1 + `pwd`/sacrist $file $event + ;; + DELETE) + echo "Removed $file..."; sleep 1 + `pwd`/sacrist $file $event + ;; + esac + fi +done diff --git a/src/sacrist b/src/sacrist @@ -0,0 +1,63 @@ +#!/usr/bin/env zsh +# +# sacrist - script called by mourner, for our graveyard administration +# +# ~ parazyd + +zkv=1 +helpers=1 + +R=$(pwd) +source $R/zlibs/zuper + +vars+=(device keyuuid) + +source $R/zlibs/features +source $R/zlibs/hooks +source $R/zlibs/keyfiles +source $R/zlibs/mounts +source $R/zlibs/ttab + +source $R/zlibs/zuper.init + +device=$1 +happenz=$2 +keyuuid=$(lsblk -no uuid $device) + +typeset -H keypass +typeset -H keyuuid +typeset -H undertaker +typeset -H graveyard +typeset -H tombs +typeset -H tombpasswd + +# Vars +graveyard="/home/graveyard" # Our graveyard, with all the tombs +tombs="$graveyard/tombs" # Info about opened tombs, holds keyuuid, keyhash and tombid +tmptombs="$graveyard/tmptombs" # Temp tempfile, for updating $tombs +keymount="/media/tombkey" # Directory where keys get mounted +coffindot="$keymount/.coffin" # .coffin directory on the usb key +ttab="$coffindot/ttab" # Our ttab +hooks="$coffindot/hook" +tomb="/usr/local/bin/tomb" +tombpasswd="$graveyard/passwd" + +[[ $happenz == "CREATE" ]] && { + mount-key + + if [[ -d "$coffindot" ]]; then + notice "Found .coffin" + + [[ -f "$hooks" ]] && check-hooks + [[ -f "$ttab" ]] && ttab-magic + + else + _msg warning "No .coffin directory" + fi + + umount-key +} + +[[ $happenz == "DELETE" ]] && { + # Some kind of endgame +} diff --git a/src/zlibs/features b/src/zlibs/features @@ -0,0 +1,45 @@ +#!/usr/bin/env zsh + +check-webdav-hook() { + fn check-webdav-hook + + davconf="/etc/apache2/sites-available/tomboxdav.conf" + + if [[ $entry =~ webdav && -f $coffindot/webdav.conf ]]; then + notice "Found WebDAV data. Setting up..." + + [[ -f $coffindot/davinfo ]] && { + cat $coffindot/davinfo >> /etc/apache2/davpasswd + [[ $? = 0 ]] && { + rm $coffindot/davinfo + gpasswd -a www-data $undertaker + notice "Added new WebDAV user" + } + } + sed -i -e :a -e '$d;N;2,3ba' -e 'P;D' $davconf + cat $coffindot/webdav.conf >> $davconf + notice "Wrote to $davconf" + /etc/init.d/apache2 restart + notice "Done setting up WebDAV" + else + notice "No WebDAV data found" + fi +} + +check-sshfs-hook() { + fn check-sshfs-hook + + if [[ $entry =~ sshfs && -f $coffindot/sshpubkey ]]; then + notice "Found SSH data. Setting up..." + + mkdir -p /home/$undertaker/.ssh + cat $coffindot/sshpubkey >> /home/$undertaker/.ssh/authorized_keys + chown -R $undertaker:$undertaker /home/$undertaker/.ssh + chmod 700 /home/$undertaker/.ssh && chmod 600 /home/$undertaker/.ssh/authorized_keys + + [[ $? = 0 ]] && notice "Wrote to authorized_keys" \ + && notice "Done setting up SSH" + else + notice "No SSH data found" + fi +} diff --git a/src/zlibs/hooks b/src/zlibs/hooks @@ -0,0 +1,80 @@ +#!/usr/bin/env zsh + +check-hooks() { + # TODO: fragmented keys, delete, backup, moar + fn check-hooks + + for entry in $(cat $hooks); do + let hook=$hook+1 + notice "Found hook $hook..." + + # Check what's hook supposed to do + if [[ ${entry[(ws@:@)1]} == "create" ]]; then + create-new-tomb + elif [[ ${entry[(ws@:@)1]} == "delete" ]]; then + delete-tomb + elif [[ ${entry[(ws@:@)1]} == "backup" ]]; then + backup-tomb + else + _msg failure "No valid hook syntax on hook $hook" + print $entry >> $hooks.fail + notice "Wrote failed hook to $hooks.fail" + fi + done +} + +create-new-tomb() { + fn create-new-tomb + + # TODO: recognize custom post/bind hooks and implement them in the + # new tomb + + notice "Creating new tomb" + + undertaker=${entry[(ws@:@)2]} + tombid=${entry[(ws@:@)3]} + tombsize=${entry[(ws@:@)4]} + keypass=$(pwgen 30 -1 1) + + if ! [[ ( $(id $undertaker) ) ]]; then + _msg warning "No user called $undertaker found. Creating..." + useradd -G tombox -m -s /bin/nologin $undertaker + notice "Created user $undertaker" + else + notice "Found user $undertaker" + fi + + # Check for features + check-webdav-hook + check-sshfs-hook + + notice "Digging your tomb..." + + sudo -u $undertaker $tomb dig -s $tombsize $graveyard/$tombid.tomb || \ + (_msg failure "Digging went downhill. Cleaning and exiting" && \ + clean-failed-hook) + + sudo -u $undertaker $tomb forge $graveyard/$tombid.key \ + --unsafe \ + --tomb-pwd "$keypass" || \ + (_msg failure "Forging key went downhill. Cleaning and exiting" && \ + clean-failed-hook) + + sudo -u $undertaker $tomb lock $graveyard/$tombid.tomb \ + -k $graveyard/$tombid.key \ + --unsafe \ + --tomb-pwd "$keypass" || \ + (_msg failure "Locking tomb went downhill. Cleaning and exiting" && \ + clean-failed-hook) + + notice "Moving your keyfile to your USB key..." + mv $graveyard/$tombid.key $coffindot/ && \ + chown $undertaker:$undertaker $coffindot/$tombid.key && \ + notice "Moved and chowned keyfile" + + print "${undertaker}:${tombid}:true" >> $ttab + + keyhash=$(hash-key) + print "${keyhash}:${keypass}" >> $tombpasswd + notice "Wrote to ttab and tombpasswd" +} diff --git a/src/zlibs/keyfiles b/src/zlibs/keyfiles @@ -0,0 +1,19 @@ +#!/usr/bin/env zsh + +hash-key() { + fn hash-key + + ${$(sha256sum $coffindot/$tombid.key)[(ws: :)1]} +} + +compare-key() { + fn compare-key + + keyhash=$(hash-key) + + if [[ ( $(grep $keyhash $tombs | grep $keyuuid) ) ]]; then + return 0 + else + return 1 + fi +} diff --git a/src/zlibs/mounts b/src/zlibs/mounts @@ -0,0 +1,31 @@ +#!/usr/bin/env zsh + +mount-key() { + fn mount-key + + if [[ -d $keymount ]]; then + _msg failure "$keymount already exists." + return 1 + else + notice "Creating $keymount" + mkdir -p $keymount + notice "Mounting..." + mount $device $keymount + return 0 + fi +} + +umount-key() { + fn umount-key + + if [[ -d $keymount ]]; then + notice "Unmounting $keymount" + umount $keymount \ + && rmdir $keymount + notice "Success umounting" + return 0 + else + notice "No $keymount found" + return 0 + fi +} diff --git a/src/zlibs/ttab b/src/zlibs/ttab @@ -0,0 +1,54 @@ +#!/usr/bin/env zsh + +ttab-magic() { + fn ttab-magic + + notice "Doing ttab magic..." + + line=0 + + for entry in $(cat $ttab); do + let line=$line+1 + notice "Found line $line..." + + [[ ${entry[(ws@:@)3]} == "true" ]] && { + notice "Working on tomb from line $line" + + undertaker=${entry[(ws@:@)1]} + notice "Username: $undertaker" + tombid=${entry[(ws@:@)2]} + notice "Tombname: $tombid" + + compare-key + + [[ $? = 0 ]] && { + notice "compare-key -> true" + sudo -u $undertaker $tomb slam $tombid + + cp $tombs $tmptombs + grep -v $keyhash $tmptombs > $tombs && \ + chmod 600 $tombs && \ + notice "Updated $tombs"$tombs + rm $tmptombs + continue + } + + notice "compare-key -> false" + + keypass=${$(grep $keyhash $tombpasswd)[(ws@:@)2]} + + sudo -u $undertaker $tomb open $graveyard/$tombid.tomb \ + -k $coffindot/$tombid.key \ + --unsafe \ + --tomb-pwd "$keypass" + + chmod g+rw /media/$tombid + + [[ -d "/media/$tombid" ]] && { + print "${undertaker}:${keyhash}:${keyuuid}" >> $tombs && \ + chmod 600 $tombs && \ + notice "Added info to $tombs" + } + } + done +} diff --git a/src/zlibs/zuper b/src/zlibs/zuper @@ -0,0 +1,729 @@ +#!/usr/bin/env zsh +## -*- origami-fold-style: triple-braces -*- +# +# Zuper - Zsh Ultimate Programmer's Extensions Refurbished +# +# Copyright (C) 2015 Dyne.org Foundation +# +# Zuper is designed, written and maintained by Denis Roio <jaromil@dyne.org> +# +# This source code is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This source code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# Please refer to the GNU Public License for more details. +# +# You should have received a copy of the GNU Public License along with +# this source code; if not, write to: +# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +########################## +typeset -aU vars +typeset -aU arrs +typeset -aU maps + +vars=(DEBUG QUIET LOG) +arrs=(req freq) + +vars+=(zuper_version) +zuper_version=0.2 + +# {{{ Messaging + +# Messaging function with pretty coloring +autoload colors +colors + +vars+=(last_act last_func last_notice) + +function _msg() { + local msg="$2" + command -v gettext 1>/dev/null 2>/dev/null && msg="$(gettext -s "$2")" + for i in $(seq 3 ${#}); + do + msg=${(S)msg//::$(($i - 2))*::/$*[$i]} + done + + local command="print -P" + local progname="$fg[magenta]${PROGRAM##*/}$reset_color" + local message="$fg_bold[normal]$fg_no_bold[normal]$msg$reset_color" + local -i returncode + + case "$1" in + inline) + command+=" -n"; pchars=" > "; pcolor="yellow" + ;; + message) + last_act="$msg" + pchars=" . "; pcolor="white"; message="$fg_no_bold[$pcolor]$msg$reset_color" + ;; + verbose) + last_func="$msg" + pchars="[D]"; pcolor="blue" + ;; + success) + last_notice="$msg" + pchars="(*)"; pcolor="green"; message="$fg_no_bold[$pcolor]$msg$reset_color" + ;; + warning) + pchars="[W]"; pcolor="yellow"; message="$fg_no_bold[$pcolor]$msg$reset_color" + ;; + failure) + pchars="[E]"; pcolor="red"; message="$fg_no_bold[$pcolor]$msg$reset_color" + returncode=1 + ;; + print) + progname="" + ;; + *) + pchars="[F]"; pcolor="red" + message="Developer oops! Usage: _msg MESSAGE_TYPE \"MESSAGE_CONTENT\"" + returncode=127 + zerr + ;; + esac + ${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >&2 + + # write the log if its configured + [[ "$LOG" = "" ]] || { + touch $LOG || return $? + ${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >> $LOG + } + + return $returncode +} + +function _message say act() { + local notice="message" + [[ "$1" = "-n" ]] && shift && notice="inline" + [[ $QUIET = 1 ]] || _msg "$notice" $@ + return 0 +} + +function _verbose xxx func() { + [[ $DEBUG = 1 ]] && _msg verbose $@ + return 0 +} + +function _success yes notice() { + [[ $QUIET = 1 ]] || _msg success $@ + return 0 +} + +function _warning no warn warning() { + [[ $QUIET = 1 ]] || _msg warning $@ + return 0 +} + +function _failure fatal die error() { + # typeset -i exitcode=${exitv:-1} + [[ $QUIET = 1 ]] || _msg failure $@ + return 1 +} + +function _print() { + [[ $QUIET = 1 ]] || _msg print $@ + return 0 +} + +# }}} Messaging + +# {{{ Debugging + +fn() { + fun="$@" + req=() + freq=() + func "$fun" +} + +zerr() { + error "error in: ${fun:-$last_notice}" + [[ "$last_func" = "" ]] || warn "called in: $last_func" + [[ "$last_act" = "" ]] || warn "called in: $last_act" + [[ "$last_notice" = "" ]] || warn "called in: $last_notice" + # [[ "$fun" = "" ]] || warn "called in: $fun" + TRAPEXIT() { + error "error reported, operation aborted." + } + return 1 +} + + +ckreq reqck() { + err=0 + for v in $req; do + [[ "${(P)v}" = "" ]] && { + warn "${fun[(ws: :)1]}(): required setting is blank: $v" + err=1 + } + done + + [[ $err = 1 ]] && return $err + + for f in $freq; do + # exists and has size greater than zero + [[ -s $f ]] || { + warn "required file empty: $f" + err=1 + } + done + [[ $err == 1 ]] && zerr + return $err +} + +zdump() { + fn zdump + [[ ${#vars} -gt 0 ]] && { + print "Global variables:" + for _v in $vars; do + print " $_v = \t ${(P)_v}" + done + } + [[ ${#arrs} -gt 0 ]] && { + print "Global arrays:" + for _a in $arrs; do + print " $_a \t ( ${(P)_a} )" + done + } + [[ ${#maps} -gt 0 ]] && { + print "Global maps:" + for _m in $maps; do + print " $_m [key] \t ( ${(Pk)_m} )" + print " $_m [val] \t ( ${(Pv)_m} )" + done + } +} + +# handy wrappers for throw/catch execution of blocks where we need the +# program to exit on any error (non-zero) returned by any function +throw() { function TRAPZERR() { zerr; return 1 } } +catch() { function TRAPZERR() { } } + +########################## +# Endgame handling + +arrs+=(destruens) +destruens=() + +# Trap functions for the endgame event +TRAPINT() { endgame INT; return $? } +# TRAPEXIT() { endgame EXIT; return $? } +TRAPHUP() { endgame HUP; return $? } +TRAPQUIT() { endgame QUIT; return $? } +TRAPABRT() { endgame ABORT; return $? } +TRAPKILL() { endgame KILL; return $? } +# TRAPPIPE() { endgame PIPE; return $? } +TRAPTERM() { endgame TERM; return $? } +TRAPSTOP() { endgame STOP; return $? } +# TRAPZERR() { func "function returns non-zero." } + + +endgame() { + fn "endgame $*" + + # execute all no matter what + TRAPZERR() { } + + # process registered destructors + for d in $destruens; do + fn "destructor: $d" + $d + done + return 0 +} + +# Register endgame() to be called at exit. +# unlike TRAPEXIT, the zshexit() hook is not called when functions exit. +zshexit() { endgame EXIT; return $? } + +# }}} Debugging + +# {{{ Tempfiles + +########################## +# Temp file handling + +vars+=(ztmpfile) +# ztmp() fills in $ztmpfile global. Caller must copy that variable as +# it will be overwritten at every call. +ztmp() { + fn ztmp + + ztmpfile=`mktemp` + tmpfiles+=($ztmpfile) +} + +# All tempfiles are freed in endgame() +_ztmp_destructor() { + fn _ztmp_destructor + + for f in $tmpfiles; do + rm -f "$f" + done + tmpfiles=() +} + +arrs+=(tmpfiles) +destruens+=(_ztmp_destructor) + +# }}} Tempfiles + +# {{{ Strings + +# tokenizer, works only with one char length delimiters +# saves everything in global array tok=() +arrs+=(tok) +strtok() { + fn "strtok $*" + _string="$1" + _delim="$2" + req=(_string _delim) + ckreq || return $? + + tok=() + f=0 + c=0 + for c in {1..${#_string}}; do + if [[ "${_string[(e)$c]}" == "$_delim" ]]; then + # check if not empty + t=${_string[(e)$(($f + 1)),$(($c - 1))]} + [[ "$t" == "" ]] || tok+=($t) + # save last found + f=$c + fi + done + # add last token + t=${_string[(e)$(($f + 1)),$c]} + [[ "$t" == "" ]] || tok+=($t) +} + +# TODO: move in here some helpers + +# }}} Strings + +# {{{ Key/Value filesave + +# optional: define zkv=1 on source + +[[ "$zkv" = "" ]] || { + + ########################## + # Key/Value file storage using ZSh associative maps + + zmodload zsh/system + + # load a map from a file + # map must be already instantiated with typeset -A by called + # name of map is defined inside the file + function zkv.load() { + fn "zkv-load $*" + + file=$1 + [[ "$file" = "" ]] && { + error "zkv-open() missing argument: file-path" + zerr + return 1 } + [[ -r "$file" ]] || { + error "zkv-open() file not found $file" + zerr + return 1 } + [[ -s "$file" ]] || { + error "zkv-open() file is empty" + zerr + return 1 } + + source $file + } + + # save a map in a file + # $1 = name of the map associative array + # $2 = full path to the file + function zkv.save() { + fn "zkv.save $*" + + _map=$1 + _path=$2 + [[ "$_path" = "" ]] && { + error "zkv.save() missing argument: map-name path-to-file" + zerr + return 1 + } + [[ -r $_path ]] && { + func "zkv.close() overwriting $_path" + func "backup turd left behind: ${_path}~" + mv $_path $_path~ + } + touch $_path + + # wondering about http://www.zsh.org/mla/users/2015/msg00286.html + # meanwhile solved using a double array, wasting a full map memcpy + _karr=(${(Pk)_map}) + _varr=(${(Pv)_map}) + _num="${#_karr}" + for c in {1..$_num}; do + # can also be cat here, however for speed we use builtins + # switch to cat if compatibility is an issue + sysread -o 1 <<EOF >> $_path +$_map+=("${_karr[$c]}" "${(v)_varr[$c]}") +EOF + done + func "$_num key/values stored in $_path" + } + +} + +# }}} Key/Value filesave + +# {{{ Get/Set REST API + +# optional: define restful=1 on source + +[[ "$restful" = "" ]] || { + + ######## + # Restful API client + # there is a clear zsh optimization here in get/set kv + # using zsh/tcp instead of spawning curl + # and perhaps querying with one call using ?recursive + + zmodload zsh/net/tcp + + + function restful.put() { + fn "restful.put $*" + + # $1 = hostname + # $2 = port + # $3 = path + # value from stdin | + + # to check if the http service is running is up to the caller + + _host=${1} # ip address + _port=${2} + _path=${3} + sysread _v + + req=(_host) + ckreq || return $? + + if ztcp $_host $_port; then + + # TODO: work out various parsers, this one works with consul.io + + _fd=$REPLY + # func "tcp open on fd $fd" + cat <<EOF >& $_fd +PUT ${_path} HTTP/1.1 +User-Agent: Zuper/$zuper_version +Host: ${_host}:${_port} +Accept: */* +Content-Length: ${#_v} +Content-Type: application/x-www-form-urlencoded + +EOF + + print -n "$_v" >& $_fd + + sysread -i $_fd _res + + # close connection + ztcp -c $_fd + + [[ "$_res" =~ "true" ]] || { + warn "failed PUT on restful key/value" + warn "host: ${_host}" + warn "port: ${_port}" + warn "path: ${_path}" + warn "value: $_v" + print - "$_res" + zerr + return 1 + } + + else + error "cannot connect to restful service: $_host:$_port" + zerr + return 1 + fi + + return 0 + + } + + function restful.get() { + fn "restful.get $*" + + _host=${1} + _port=${2} + _path=${3} + + req=(_host _port) + ckreq || return $? + + ztcp $_host $_port || { + zerr + return 1 + } + + _fd=$REPLY + + # TODO: work out various parsers, this one works with consul.io + + cat <<EOF >& $_fd +GET ${_path} HTTP/1.1 +User-Agent: Zuper/$zuper_version +Host: $_host:$_port +Accept: */* + +EOF + sysread -i $_fd -o 1 | awk -F: ' +/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d + + # close connection + ztcp -c $_fd + + return 0 + + } + +} + +# }}} Get/Set REST API + +# {{{ Helpers +[[ "$helpers" = "" ]] || { + + function helper.isfound isfound() { + command -v $1 1>/dev/null 2>/dev/null + return $? + } + + # remote leading and trailing spaces in a string taken from stdin + function helper.trim trim() { + sed -e 's/^[[:space:]]*//g ; s/[[:space:]]*\$//g' + } + + zmodload zsh/mapfile + # faster substitute for cat + function helper.printfile printfile() { + print ${mapfile[$1]} + } + + # extract all emails found in a text from stdin + # outputs them one per line + function helper.extract-emails extract_emails() { + awk '{ for (i=1;i<=NF;i++) + if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) { + gsub(/<|>|,/ , "" , $i); print $i } }' + } + + + zmodload zsh/regex + # takes a string as argument, returns success if is an email + function helper.isemail isemail() { + [[ "$1" -regex-match "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" ]] && return 0 + + return 1 + } + + # takes a numeric argument and prints out a human readable size + function helper.human-size human_size() { + [[ $1 -gt 0 ]] || { + error "human_size() called with invalid argument" + return 1 + } + + # we use the binary operation for speed + # shift right 10 is divide by 1024 + + # gigabytes + [[ $1 -gt 1073741824 ]] && { + print -n "$(( $1 >> 30 )) GB" + return 0 + } + + # megabytes + [[ $1 -gt 1048576 ]] && { + print -n "$(( $1 >> 20 )) MB" + return 0 + } + # kilobytes + [[ $1 -gt 1024 ]] && { + print -n "$(( $1 >> 10 )) KB" + return 0 + } + # bytes + print -n "$1 Bytes" + return 0 + } + + + # strips out all html/xml tags (everything between < >) + function helper.html-strip xml_strip html_strip() { sed 's/<[^>]\+>//g' } + + # changes stdin string special chars to be shown in html + function helper.escape-html escape_html() { + sed -e ' +s/\&/\&amp;/g +s/>/\&gt;/g +s/</\&lt;/g +s/"/\&quot;/g +' + } + + # escapes special chars in urls + function helper.decode-url decode_url urldecode() { + sed -e ' +s/%25/%/gi +s/%20/ /gi +s/%09/ /gi +s/%21/!/gi +s/%22/"/gi +s/%23/#/gi +s/%24/\$/gi +s/%26/\&/gi +s/%27/'\''/gi +s/%28/(/gi +s/%29/)/gi +s/%2a/\*/gi +s/%2b/+/gi +s/%2c/,/gi +s/%2d/-/gi +s/%2e/\./gi +s/%2f/\//gi +s/%3a/:/gi +s/%3b/;/gi +s/%3d/=/gi +s/%3e//gi +s/%3f/?/gi +s/%40/@/gi +s/%5b/\[/gi +s/%5c/\\/gi +s/%5d/\]/gi +s/%5e/\^/gi +s/%5f/_/gi +s/%60/`/gi +s/%7b/{/gi +s/%7c/|/gi +s/%7d/}/gi +s/%7e/~/gi +s/%09/ /gi +' + } + + function helper.encode-url encode_url urlencode() { + sed -e ' +s/%/%25/g +s/ /%20/g +s/ /%09/g +s/!/%21/g +s/"/%22/g +s/#/%23/g +s/\$/%24/g +s/\&/%26/g +s/'\''/%27/g +s/(/%28/g +s/)/%29/g +s/\*/%2a/g +s/+/%2b/g +s/,/%2c/g +s/-/%2d/g +s/\./%2e/g +s/\//%2f/g +s/:/%3a/g +s/;/%3b/g +s//%3e/g +s/?/%3f/g +s/@/%40/g +s/\[/%5b/g +s/\\/%5c/g +s/\]/%5d/g +s/\^/%5e/g +s/_/%5f/g +s/`/%60/g +s/{/%7b/g +s/|/%7c/g +s/}/%7d/g +s/~/%7e/g +s/ /%09/g +' + } + +} +# }}} Helpers + +# {{{ Config + +# This is not a full config parser, but its a mechanism to read single +# sections of configuration files that are separated using various +# syntax methods. The only method supported is now org-mode whose +# sections start with #+ . It fills in the global array +# $config_section which can be read out to a file or interpreted in +# memory, whatever syntax it may contain. + +vars+=(config_section_type) +arrs+=(config_section) +config_section_type=org-mode + +config.section.type() { + fn config.section.type + _type=$1 + req=(_type) + ckreq || return $? + + case $_type in + org-mode) + config_section_type=org-mode + ;; + *) + error "Unknown config type:$_type" + return 1 + ;; + esac + + act "$_type config section parser initialized" + return 0 + +} + +# fills in contents of section in array config_section +config.section.read() { + fn config.section.read + _file=$1 + _section=$2 + req=(_file _section) + freq=($_file) + ckreq || return $? + + case $config_section_type in + org-mode) + _contents=`awk ' +BEGIN { found=0 } +/^#\+ '"$_section"'/ { found=1; next } +/^#\+/ { if(found==1) exit 0 } +/^$/ { next } +{ if(found==1) print $0 } +' $_file` + + ;; + *) + error "Unknown config type:$_type" + ;; + esac + + config_section=() + for c in ${(f)_contents}; do + config_section+=("$c") + done + return 0 + +} + +# }}} Config diff --git a/src/zlibs/zuper.init b/src/zlibs/zuper.init @@ -0,0 +1,35 @@ +########################## +# Zuper Init + +# initialize globals only after sourcing everything +# since zlibs may contain more variable declarations +for _v in $vars; do + typeset -h $_v +done +for _a in $arrs; do + typeset -aU $_a +done +for _m in $maps; do + typeset -A $_m +done + +# reset defaults +DEBUG=${DEBUG:-0} +QUIET=${QUIET:-0} +LOG=${LOG:-""} +req=() +freq=() +last_act=() +last_func=() +last_notice=() +tmpfiles=() +config_section=() +config_section_type=${config_section_type:-org-mode} + + +func "Zuper $zuper_version initialized" +func "${#vars} global variables registered" +func "${#arrs} global arrays registered" +func "${#maps} global maps registered" + +