Initial commit.

This commit is contained in:
Manuel Amador (Rudd-O) 2017-05-07 14:27:08 +00:00
commit 4314d40696
9 changed files with 469 additions and 0 deletions

31
Makefile Normal file
View File

@ -0,0 +1,31 @@
BINDIR=/usr/bin
SYSCONFDIR=/etc
DESTDIR=
PROGNAME=qubes-pass
clean:
find -name '*~' -print0 | xargs -0 rm -fv
rm -fv *.tar.gz *.rpm
dist: clean
excludefrom= ; test -f .gitignore && excludefrom=--excludefrom=.gitignore ; DIR=$(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec` && FILENAME=$$DIR.tar.gz && tar cvzf "$$FILENAME" --exclude="$$FILENAME" --exclude=.git --exclude=.gitignore $$excludefrom --transform="s|^|$$DIR/|" --show-transformed *
rpm: dist
T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ta $(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/RPMS/*/* "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T"
srpm: dist
T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ts $(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T"
install-client:
install -Dm 755 bin/qvm-pass -t $(DESTDIR)/$(BINDIR)/
install -Dm 755 bin/qubes-pass-client -t $(DESTDIR)/$(BINDIR)/
install-service:
install -Dm 644 etc/qubes-rpc/ruddo.PassRead -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/
install -Dm 644 etc/qubes-rpc/ruddo.PassManage -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/
install-dom0:
install -Dm 664 etc/qubes-rpc/policy/ruddo.PassRead -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/
getent group qubes && chgrp qubes $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/ || true
install -Dm 664 etc/qubes-rpc/policy/ruddo.PassManage -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/
getent group qubes && chgrp qubes $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/ || true

63
README.md Normal file
View File

@ -0,0 +1,63 @@
# Inter-VM Pass password manager for Qubes OS
This is a very simple bridge between Qubes OS VMs. With it, you can
store and retrieve passwords between VMs without having to grant
any of the VMs any special policy privileges other than access to the
Qubes services implemented here.
## Using the software
These instructions assume you have installed the software. See the
*Installing the software* heading below for more information.
Run `qvm-pass -?` on a terminal to get usage information.
## Installing the software
There are three components for this software:
* The client `qvm-pass-client` you install in the VMs (or their templates)
where you want to manage your passwords.
* The service `qvm-pass-service` you install in the VMs (or their templates)
where you want to store your passwords.
* The policy `qvm-pass-dom0` is the dom0 side of things, necessary to
enable the services and control access from the clients to the service.
First, build the software, After cloning this repository on a suitable VM,
run the command:
```
make rpm
```
This will generate three installable packages on the local directory:
* `qvm-pass-client-<version>.noarch.rpm`
* `qvm-pass-service-<version>.noarch.rpm`
* `qvm-pass-dom0-<version>.noarch.rpm`
Copy the `qvm-pass-client-<version>.noarch.rpm` file to the template VM
or standalone VM where you plan to manage passwords. Install the RPM with
`dnf install <name of the RPM>`. At this point, this VM, as well as
any VMs using this as a template, have gained the ability to list
and store passwords stored in other VMs.
Now copy the `qvm-pass-service-<version>.noarch.rpm` file to the template
VM or standalone VM where you plan to store passwords. Install the RPM with
`dnf install <name of the RPM>`. At this point, this VM, as well as
any VMs using this as a template, have gained the ability to securely store
passwords in `/home/user/.password-store`.
Now copy the `qvm-pass-policy-dom0-<version>.noarch.rpm` file to
your dom0. At this point, the default policy (`ask`) is active on
your Qubes OS system, and you can begin using the client.
Those clever among you will have discovered that there is a `Makefile`
included, and that you can use the `Makefile` to install the software on
other non-RPM templates. I welcome pull requests to add support for
other distro packages and Qubes OS templates.
## Troubleshooting and debugging
As always, you can file new issues on the repo of this project for help
with fixing bugs that the programs may have. Pull requests also welcome.

52
bin/qubes-pass-client Executable file
View File

@ -0,0 +1,52 @@
#!/bin/bash
set -e
if [ -s /rw/config/pass-split-domain -a -z "$QUBES_PASS_DOMAIN" ] ; then
export QUBES_PASS_DOMAIN=$( cat /rw/config/pass-split-domain )
fi
if [ -z "$QUBES_PASS_DOMAIN" ] ; then
title="Qubes pass error"
msg="The QUBES_PASS_DOMAIN variable is not defined. Either create /rw/config/pass-split-domain with the VM containing your pass setup, set the environment variable yourself, or pass -d on the command line."
echo "$title: $msg" >&2
zenity --error --text "$msg" --title "$title" &
exit 124
fi
if [ "$1" == "list" ] ; then
cmd=$(echo "$1" | base64)
echo "$cmd" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassRead
elif [ "$1" == "get" ] ; then
cmd=$(echo "$1" | base64)
key=$(echo "$2" | base64)
echo "$cmd
$key" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassRead
elif [ "$1" == "get-or-generate" ] ; then
cmd=$(echo "$1" | base64)
key=$(echo "$2" | base64)
autogen=$(echo 1 | base64)
echo "$cmd
$key
$autogen" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassManage
elif [ "$1" == "insert" ] ; then
cmd=$(echo "$1" | base64)
key=$(echo "$2" | base64)
multiline=$(echo "$3" | base64)
contents=$(echo "$4" | base64)
echo "$cmd
$key
$multiline
$contents" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassManage
elif [ "$1" == "init" ] ; then
cmd=$(echo "$1" | base64)
echo "$cmd" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassManage
fi

124
bin/qvm-pass Executable file
View File

@ -0,0 +1,124 @@
#!/bin/bash
TEMP=`getopt -o ?dmfe: -- "$@"`
force=0
multiline=0
echo=0
eval set -- "$TEMP"
set -e
usage() {
echo "qvm-pass usage:"
echo ""
echo " qvm-pass [-d <passvm>] <subcommand> [arguments...]"
echo ""
echo "subcommands:"
echo ""
echo " list"
echo " Retrieves the list of keys from the pass store."
echo " No subcommand accomplishes the same results"
echo " get <key>"
echo " Retrieves a key from the pass store."
echo " If your key is not named after a subcommand, you can also"
echo " get its contents by passing it as the first argument of"
echo " this command, omitting the get subcommand."
echo " get-or-generate <key>"
echo " Retrieves a key from the pass store; creates the key"
echo " with 32 characters length if it does not exist yet,"
echo " and returns the generated key on standard output."
echo " insert [--echo,-e | --multiline,-m] [--force,-f] <key>"
echo " Creates a key in the pass store."
exit 0
}
while true ; do
case "$1" in
-d)
case "$2" in
"") shift 2 ;;
*) export QUBES_PASS_DOMAIN="$2" ; shift 2 ;;
esac ;;
-m)
multiline=1 ; shift ;;
-f)
force=1 ; shift ;;
-e)
echo=1 ; shift ;;
"-?")
usage ;;
--) shift ; break ;;
*) echo "error processing options; run with -? for more information" ; exit 64 ;;
esac
done
case "$1" in
get|get-or-generate)
if [ "$force$multiline$echo" != "000" ] ; then
echo "the $1 subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
fi
if [ -z "$2" ] ; then
echo "the $1 subcommand requires a key; run with -? for more information" >&2 ; exit 64
fi
if [ -n "$3" ] ; then
echo "the $1 subcommand only accepts one argument; run with -? for more information" >&2 ; exit 64
fi
exec qubes-pass-client "$1" "$2"
;;
init)
if [ "$force$multiline$echo" != "000" ] ; then
echo "the $1 subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
fi
if [ -n "$2" ] ; then
echo "the $1 subcommand does not accept any arguments; run with -? for more information" >&2 ; exit 64
fi
exec qubes-pass-client "$1"
;;
insert)
shift
if [ "$force" != "1" ] ; then
ret=0 ; errs=$(qubes-pass-client get "$1" >/dev/null 2>&1) || ret=$?
if [ "$ret" == "0" ] ; then
read -p "An entry already exists for $1. Overwrite it? [y/N] " response
if [ "$response" != "y" ] ; then exit 0 ; fi
elif [ "$ret" == "8" ] ; then
true
else
echo "$errs" >&2
exit $ret
fi
fi
contents=
if [ "$multiline" == "1" ] ; then
echo "Enter contents of $1 and press Ctrl+D when finished:"
echo ""
contents=$(cat)
elif [ "$echo" == "1" ] ; then
read -p "Enter password for b: " contents >&2
else
read -s -p "Enter password for b: " contents >&2
echo
read -s -p "Retype password for b: " retypedcontents >&2
echo
if [ "$retypedcontents" != "$contents" ] ; then
echo "Error: the entered passwords do not match."
exit 1
fi
fi
exec qubes-pass-client insert "$1" "$multiline" "$contents"
;;
list)
if [ "$force$multiline$echo" != "000" ] ; then
echo "the $1 subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
fi
exec qubes-pass-client list
;;
*)
if [ "$force$multiline$echo" != "000" ] ; then
echo "the get subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
fi
exec qubes-pass-client get "$1"
;;
esac

View File

@ -0,0 +1,6 @@
## Note that policy parsing stops at the first match,
## so anything below the last line will have no effect.
## Please use a single # to start your custom comments.
$anyvm $anyvm ask

View File

@ -0,0 +1,6 @@
## Note that policy parsing stops at the first match,
## so anything below the last line will have no effect.
## Please use a single # to start your custom comments.
$anyvm $anyvm ask

View File

@ -0,0 +1,80 @@
#!/bin/bash
set -e
read -n 4096 cmd
cmd=$(echo "$cmd" | base64 -d)
if [ "$cmd" == "init" ] ; then
if test -f "$HOME"/.password-store/.gpg-id ; then
key=$(cat "$HOME"/.password-store/.gpg-id)
echo "Not creating password store already exists and uses GPG key $key." >&2
exit 8
fi
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
cat > "$tmp" <<EOF
Key-Type: RSA
Key-Length: 4096
Name-Real: Pass store
Name-Email: noreply@passwordstore.org
Expire-Date: 0
EOF
ret=0 ; out=$(gpg2 --batch --gen-key "$tmp" 2>&1) || ret=$?
if [ "$ret" != "0" ] ; then
echo "$out" >&2
exit "$ret"
fi
key=$(echo "$out" | awk '/gpg: key .* marked as ultimately trusted/ { print $3 }')
pass init "$key"
echo "Do not forget to back up your password store regularly." >&2
echo "To back up your password store, back up the entire $HOSTNAME VM using Qubes backup." >&2
echo "Key files to backup: $HOME/.password-store and $HOME/.gnupg2" >&2
elif [ "$cmd" == "get-or-generate" ] ; then
read -n 4096 entry
read -n 4096 autogen
entry=$(echo "$entry" | base64 -d)
autogen=$(echo "$autogen" | base64 -d)
if [ "$autogen" == "1" ] ; then
ret=0 ; out=$(pass -- "$entry") || ret=$?
if [ "$ret" == "1" ] && echo "$out" | grep -q "not in the password store" ; then
logger -t ruddo.PassManage "creating password entry $entry"
ret=0 ; out=$(pass generate -- "$entry" 32) || ret=$?
if [ "$ret" == "1" ] ; then
echo "Password generation failed: $out"
exit "$ret"
fi
elif [ "$ret" != "0" ] ; then
echo "$out" >&2
exit "$ret"
fi
logger -t ruddo.PassManage "requested password entry $entry"
exec pass -- "$entry"
else
exit 23
fi
elif [ "$cmd" == "insert" ] ; then
read -n 4096 entry
read -n 4096 multiline
read -n 1048576 contents
entry=$(echo "$entry" | base64 -d)
multiline=$(echo "$multiline" | base64 -d)
contents=$(echo "$contents" | base64 -d)
logger -t ruddo.PassManage "creating password entry $entry"
if [ "$multiline" == "1" ] ; then
echo "$contents" | pass insert --multiline --force -- "$entry"
else
echo "$contents" | pass insert -e --force -- "$entry"
fi
fi

View File

@ -0,0 +1,29 @@
#!/bin/bash
set -e
read -n 4096 cmd
cmd=$(echo "$cmd" | base64 -d)
if [ "$cmd" == "list" ] ; then
logger -t ruddo.PassRead "requested password list".
exec pass
elif [ "$cmd" == "get" ] ; then
read -n 4096 entry
entry=$(echo "$entry" | base64 -d)
logger -t ruddo.PassRead "requested password entry $entry"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
ret=0 ; pass -- "$entry" 2> "$tmp" || ret=$?
if grep -qF "$entry is not in the password store." "$tmp" ; then
cat "$tmp" >&2
exit 8
fi
cat "$tmp" >&2
exit $?
fi

78
qubes-pass.spec Normal file
View File

@ -0,0 +1,78 @@
%define debug_package %{nil}
%define mybuildnumber %{?build_number}%{?!build_number:1}
Name: qubes-pass
Version: 0.0.1
Release: %{mybuildnumber}%{?dist}
Summary: Inter-VM pass password management for Qubes OS AppVMs and StandaloneVMs
BuildArch: noarch
License: GPLv3+
URL: https://github.com/Rudd-O/%{name}
Source0: https://github.com/Rudd-O/%{name}/archive/{%version}.tar.gz#/%{name}-%{version}.tar.gz
BuildRequires: make
%package service
Summary: Service package for Qubes OS dom0s that services %{name}
Requires: pass
%package dom0
Summary: Policy package for Qubes OS dom0s that arbitrates %{name}
Requires: qubes-core-dom0-linux
%description
This package lets you setup a safe password management VM and then
manage the password store remotely from other VMs. You are meant to
install this package on the VM or template where you want to access
and manage your password store.
%description service
This package lets you serve a safe password store to other VMs.
You are meant to install this package on the VM or template where
you want to keep your password store safe.
%description dom0
This package contains the Qubes OS execution policy for the %{name} package.
You are meant to install this package on the dom0 of the machine where you
have VMs that have the %{name} package installed.
%prep
%setup -q
%build
# variables must be kept in sync with install
make DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} SYSCONFDIR=%{_sysconfdir}
%install
rm -rf $RPM_BUILD_ROOT
# variables must be kept in sync with build
for target in install-client install-service install-dom0; do
make $target DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} SYSCONFDIR=%{_sysconfdir}
done
%check
if grep -r '@.*@' $RPM_BUILD_ROOT ; then
echo "Check failed: files with AT identifiers appeared" >&2
exit 1
fi
%files
%attr(0755, root, root) %{_bindir}/qvm-pass
%attr(0755, root, root) %{_bindir}/qubes-pass-client
%doc README.md
%files service
%attr(0644, root, root) %{_sysconfdir}/qubes-rpc/ruddo.PassRead
%attr(0644, root, root) %{_sysconfdir}/qubes-rpc/ruddo.PassManage
%files dom0
%config(noreplace) %attr(0644, root, qubes) %{_sysconfdir}/qubes-rpc/policy/ruddo.PassRead
%config(noreplace) %attr(0644, root, qubes) %{_sysconfdir}/qubes-rpc/policy/ruddo.PassManage
%changelog
* Mon Oct 24 2016 Manuel Amador (Rudd-O) <rudd-o@rudd-o.com>
- Initial release.