Two new related tools.
* `mount-ephemeral' creates (and removes) a temporary filesystem,
encrypted using a fresh random key so the contents are irretrievably
lost when the host reboots or the power fails.
* `claim-dir' is a `userv' service which allows users to claim
directories in a shared filesystem without the hazardous
free-for-all that results from world writability with a sticky bit.
These go in their own separate Debian package. There's no direct link
between the two, but bundling them together provides a hint regarding
possible applications.
PACKAGE="$(PACKAGE)" VERSION="$(VERSION)" \
PYTHON="$(PYTHON)" \
bindir="$(bindir)" sbindir="$(sbindir)" \
- pkgconfdir="$(pkgconfdir)" \
+ sysconfdir="$(sysconfdir)" pkgconfdir="$(pkgconfdir)" \
pkgstatedir="$(localstatedir)/lib/$(PACKAGE)" \
pkglibdir="$(pkglibdir)" \
user="$(user)"
mv userv/distorted-keys.new userv/distorted-keys
###--------------------------------------------------------------------------
+### Secure storage management.
+
+## Ephemeral filesystem construction.
+sbin_SCRIPTS += mount-ephemeral
+EXTRA_DIST += mount-ephemeral
+
+## Directory claiming service.
+noinst_DATA += userv/claim-dir
+EXTRA_DIST += userv/claim-dir.in
+CLEANFILES += userv/claim-dir
+userv/claim-dir: userv/claim-dir.in Makefile
+ $(AM_V_at)mkdir -p userv/
+ $(SUBST) $(srcdir)/userv/claim-dir.in $(SUBSTVARS) \
+ >userv/claim-dir.new && \
+ mv userv/claim-dir.new userv/claim-dir
+
+## Configuration file.
+EXTRA_DIST += claim-dir.tab
+
+###--------------------------------------------------------------------------
### Configuration snippets.
dist_profile_DATA += profile.d/00base
EXTRA_DIST += debian/distorted-keys.install
EXTRA_DIST += debian/distorted-keys.postinst
+EXTRA_DIST += debian/claim-dir.install
+
EXTRA_DIST += debian/admin.users debian/admin.groups
###----- That's all, folks --------------------------------------------------
--- /dev/null
+### -*-conf-*-
+###
+### This file lists the available filesystems which may be claimed by users.
+### Each line has the form
+###
+### FS DIR [OPT=VALUE ...]
+###
+### The FS is a simple name for the filesystem, matched against the FILSYS
+### argument to `claim-dir'. The DIR is the directory name in which to
+### create user directories. The remaining options are as follows.
+###
+### acl=[-]{USER|%GROUP},...
+### Access control list for this filesystem. Entries prefixed with `-'
+### forbid access, other entries permit; the entries are glob patterns
+### matched against the user's name or groups. The first match wins; if
+### no entry matches, access is forbidden. Without this option, access
+### is open to all.
+###
+### mount=SCRIPT
+### If DIR is not a mount point already, run SCRIPT DIR.
+
build
distorted-keys
+claim-dir
+tmp
*.log
*.substvars
files
--- /dev/null
+usr/sbin/mount-ephemeral
+
+debian/build/userv/claim-dir /etc/userv/default.d
+claim-dir.tab /etc
This system doesn't actually do very much cryptography itself. Instead,
it uses other existing implementations, such as GnuPG, OpenSSL, and
Seccure.
+
+Package: claim-dir
+Architecture: all
+Depends: userv
+Recommends: cryptsetup, dmsetup
+Description: Allow users to claim directories on file systems
+ Machines sometimes have storage devices with useful special properties --
+ such as high performance, or secure erasure on power failure. Rather than
+ set the root of such a filesystem world-writable and sticky, thereby making
+ another filesystem as hard to use safely as `/tmp', `claim-dir' lets users
+ claim directories on such filesystems via `userv'. A newly claimed
+ directory is named after the calling user, and created readable and writable
+ only by the calling user -- so he or she can relax the permissions later if
+ necessary.
+ .
+ A script `mount-ephemeral' is included which allows the construction of an
+ ephemeral filesystem -- one which is backed by normal storage (typically in
+ `/tmp'), but encrypted using a temporary key which will be lost at reboot.
+ This script can be used to build a safe place for the storage of
+ temporary secrets.
+usr/bin
+usr/sbin/keys
+usr/lib/distorted-keys
+etc/distorted-keys
+
debian/build/userv/distorted-keys /etc/userv/default.d
debian/admin.users /etc/distorted-keys
debian/admin.groups /etc/distorted-keys
--- /dev/null
+#! /bin/sh
+###
+### Mount an ephemeral filesystem
+###
+### (c) 2012 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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. See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+
+QUIS=${0##*/}
+VERSION=1.0.0
+USAGE="usage: $QUIS [-u] [-R RANDOM] [-n BYTES] [-C CIPHER] [-H HASH]
+ [-l LABEL] [-t FSTYPE] [-b BACKING-FILE] MOUNTPOINT [SIZE]"
+
+###--------------------------------------------------------------------------
+### Parse the command line.
+
+## Set initial defaults.
+mode=mount
+cipher=aes-xts-plain
+hash=sha256
+random=/dev/random
+randbytes=512
+fail=nil
+backing=/tmp
+unset label
+
+## Report version number.
+version () { echo "$QUIS, version $VERSION"; }
+
+## Report help text.
+help () {
+ version
+ cat <<EOF
+$USAGE
+
+Options:
+ -h Show this help text.
+ -v Show the program's version number.
+ -C CIPHER Cipher to use to encrypt the filesystem [$cipher].
+ -H HASH Hash function for hashing the random data [$hash].
+ -R RANDOM Source of random bytes for key material [$random].
+ -b BACKING Where to store the ciphertext [$backing].
+ -l LABEL Device mapper label [basename of MOUNTPOINT].
+ -n RANDBYTES Number of random bytes to read for the key [$randbytes].
+ -u Unmount the filesystem, destroying all data in it.
+EOF
+}
+
+## Loop over the options.
+while getopts "C:H:R:b:hl:n:t:uv" opt; do
+ case $opt in
+ h) help; exit 0 ;;
+ v) echo "$VERSION"; exit 0 ;;
+ C) cipher=$OPTARG ;;
+ H) hash=$OPTARG ;;
+ R) random=$OPTARG ;;
+ n) randbytes=$OPTARG ;;
+ b) backing=$OPTARG ;;
+ l) label=$OPTARG ;;
+ u) mode=umount ;;
+ *) fail=t ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+case $fail,$mode,$# in
+ nil,mount,2) mntpt=$1 size=$2 ;;
+ nil,umount,1) mntpt=$1 ;;
+ *) echo >&2 "$USAGE"; exit 1 ;;
+esac
+
+## Default omitted arguments.
+case "${label+t}" in t) ;; *) label=${mntpt##*/} ;; esac
+
+###--------------------------------------------------------------------------
+### Do the job.
+
+case $mode in
+
+ mount)
+ ## Mount the filesystem.
+
+ ## Determine a name for the backing file. If BACKING is a directory then
+ ## we should make a file there and delete it once we've created a
+ ## mapping. The directory may be a shared bit of filesystem, so we must
+ ## be very careful.
+ rmbacking=nil
+ if [ -d "$backing" ]; then
+ i=0
+ while :; do
+ gorp=$(openssl rand -base64 6)
+ bkdir=$backing/mnteph.$$.$gorp
+ if mkdir >/dev/null 2>&1 -m700 "$bkdir"; then break; fi
+ i=$(( $i + 1 ))
+ if [ $i -ge 100 ]; then
+ echo >&2 "$QUIS: failed to create backing directory"
+ exit 1
+ fi
+ done
+ backing=$bkdir/fs
+ trap 'rc=$?; rm "$backing"; rmdir "$bkdir"; exit $rc' EXIT
+ trap 'exit 127' INT TERM
+ rmbacking=t
+ fi
+
+ ## Create the backing file.
+ truncate -s"$size" "$backing"
+ loop=$(losetup -f --show "$backing")
+
+ ## Attach a device-mapper entry to the file.
+ dd 2>/dev/null if="$random" bs=1 count="$randbytes" |
+ cryptsetup \
+ --cipher="$cipher" --hash="$hash" \
+ --key-file=- \
+ create "$label" "$loop"
+
+ ## Create the filesystem.
+ if spew=$(mkfs 2>&1 "/dev/mapper/$label"); then
+ :
+ else
+ rc=$?
+ echo >&2 "$QUIS: mkfs failed (rc = $rc)"
+ echo "$spew" | sed >&2 's/^/| /'
+ exit $rc
+ fi
+
+ ## Mount.
+ mount "/dev/mapper/$label" "$mntpt"
+ ;;
+
+ umount)
+ ## Unmount a filesystem.
+
+ ## Find the numbers of the loopback device.
+ deps=$(dmsetup deps "/dev/mapper/$label")
+ set -- $(echo "$deps" |
+ sed 's!^.*:.*(\([0-9]\+\),[[:space:]]*\([0-9]\+\)).*$!\1 \2!')
+ case "$#" in
+ 2) ;;
+ *)
+ echo >&2 "$QUIS: unexpected answer from \`dmsetup deps'"
+ echo "$deps" | sed >&2 's/^/| /'
+ exit 1
+ ;;
+ esac
+ maj=$1 min=$2
+
+ ## Convert that into a name.
+ dev=$(readlink /sys/dev/block/$maj:$min)
+ dev=${dev##*/}
+ case "$dev" in
+ loop*) ;;
+ *)
+ echo >&2 "$QUIS: expected a loopback device; found \`$dev'"
+ exit 1
+ ;;
+ esac
+
+ ## Unmount the filesystem.
+ umount "$mntpt"
+
+ ## Remove the cryptoloop mapping.
+ if spew=$(cryptsetup 2>&1 remove "$label"); then
+ :
+ else
+ rc=$?
+ echo >&2 "$QUIS: cryptsetup failed (rc = $rc)"
+ echo "$spew" | sed >&2 's/^/| /'
+ exit $rc
+ fi
+
+ ## Disconnect the loopback device.
+ losetup -d "/dev/$dev"
+ ;;
+
+esac
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+### -*-conf-*-
+###
+### userv service for claiming a directory in a special filesystem
+
+if ( glob service claim-dir
+ & glob service-user root
+ & grep calling-user-shell /etc/shells
+ )
+ no-suppress-args
+ null-fd 0
+ require-fd 1-2 write
+ ignore-fd 3-
+ no-set-environment
+ execute sh -c "set -e; quis=$0; \
+ case $# in \
+ 1) filsys=$1 ;; \
+ *) echo >&2 \"usage: $quis FILSYS\"; exit 1 ;; \
+ esac; \
+ foundp=nil; \
+ while read fs dir opts; do \
+ case \"$fs\" in \
+ \\#* | \"\") continue ;; \
+ \"$filsys\") foundp=t; break ;; \
+ esac; \
+ done <@sysconfdir@/claim-dir.tab; \
+ case $foundp in \
+ nil) \
+ echo >&2 \"$quis: unknown filesystem \\`$filsys'\"; \
+ exit 1 \
+ ;; \
+ esac; \
+ for opt in $opts; do \
+ arg=${opt#*=}; \
+ case \"$opt\" in \
+ acl=*) \
+ verdict=forbid acl=$arg; \
+ while :; do \
+ case \"$acl\" in ?*) ;; *) break ;; esac; \
+ case \"$acl\" in \
+ *,*) word=${acl%%,*} acl=${acl#*,} ;; \
+ *) word=$acl acl=\"\" ;; \
+ esac; \
+ case \"$word\" in \
+ -*) sense=forbid word=${word#-} ;; \
+ *) sense=allow word=$word ;; \
+ esac; \
+ case \"$word\" in \
+ %*) pat=${word#%} list=\"$USERV_GROUP $USERV_GID\" ;; \
+ *) pat=$word list=\"$USERV_USER $USERV_UID\" ;; \
+ esac; \
+ matchp=nil; \
+ for i in $list; do \
+ case \"$i\" in $pat) matchp=t; break ;; esac; \
+ done; \
+ case $matchp in t) verdict=$sense; break ;; esac; \
+ done; \
+ case $verdict in \
+ forbid) \
+ echo >&2 \"$quis: permission denied\"; \
+ exit 1 \
+ ;; \
+ esac \
+ ;; \
+ mount=*) \
+ if ! mountpoint -q \"$dir\"; then $arg \"$dir\"; fi \
+ ;; \
+ *) \
+ echo >&2 \"$quis: unknown option \\`$opt'\"; \
+ exit 1 \
+ ;; \
+ esac; \
+ done; \
+ set _ $USERV_USER; user=$2; \
+ set _ $USERV_GROUP; group=$2; \
+ cd \"$dir\"; \
+ if [ ! -d \"$user\" ]; then \
+ mkdir -m700 \"$user\"; \
+ chown \"$user:$group\" \"$user\"; \
+ fi; \
+ echo \"$dir/$USERV_USER\" \
+ " claim-dir
+fi