diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2015-12-10 18:06:16 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2015-12-11 14:50:49 +0100 |
commit | 3221356d188e6b9971fccedabf83e8c78fe3de77 (patch) | |
tree | da90a63321c9922a1243e5d5fd371916e2562b7e | |
download | ctmg-3221356d188e6b9971fccedabf83e8c78fe3de77.tar.xz ctmg-3221356d188e6b9971fccedabf83e8c78fe3de77.zip |
Initial commit1.1
-rw-r--r-- | LICENSE | 14 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | README.md | 56 | ||||
-rwxr-xr-x | ctmg.sh | 154 |
4 files changed, 236 insertions, 0 deletions
@@ -0,0 +1,14 @@ +Copyright (c) 2015 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. +Copyright (c) 2014 Laurent Ghigonis <laurent@gouloum.fr> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..439426f --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +PREFIX ?= /usr +DESTDIR ?= +BINDIR ?= $(PREFIX)/bin +LIBDIR ?= $(PREFIX)/lib +MANDIR ?= $(PREFIX)/share/man + +all: + @echo "Run \"sudo make install\" to install ctmg" + +install: + @install -v -d "$(DESTDIR)$(BINDIR)/" && install -v -m 0755 ctmg.sh "$(DESTDIR)$(BINDIR)/ctmg" + diff --git a/README.md b/README.md new file mode 100644 index 0000000..322e72b --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +## ctmg + +`ctmg` is an encrypted container manager for Linux using `cryptsetup` and various standard file system utilities. Containers have the extension `.ct` and are mounted at a directory of the same name, but without the extension. Very simple to understand, and very simple to implement; `ctmg` is a simple bash script. + +### Usage + + Usage: ctmg [ new | delete | open | close | list ] [arguments...] + ctmg new container_path container_size[units_suffix] + ctmg delete container_path + ctmg open container_path + ctmg close container_path + ctmg list + +### Examples + +#### Create a 100MiB encrypted container called "example" + + zx2c4@thinkpad ~ $ ctmg create example 100MiB + [#] truncate -s 100MiB /home/zx2c4/example.ct + [#] cryptsetup --cipher aes-xts-plain64 --key-size 512 --hash sha512 --iter-time 5000 --batch-mode luksFormat /home/zx2c4/example.ct + Enter passphrase: + [#] chown 1000:1000 /home/zx2c4/example.ct + [#] cryptsetup luksOpen /home/zx2c4/example.ct ct_example + Enter passphrase for /home/zx2c4/example.ct: + [#] mkfs.ext4 -q -E root_owner=1000:1000 /dev/mapper/ct_example + [+] Created new encrypted container at /home/zx2c4/example.ct + [#] cryptsetup luksClose ct_example + +#### Open a container, add a file, and then close it + + zx2c4@thinkpad ~ $ ctmg open example + [#] cryptsetup luksOpen /home/zx2c4/example.ct ct_example + Enter passphrase for /home/zx2c4/example.ct: + [#] mkdir -p /home/zx2c4/example + [#] mount /dev/mapper/ct_example /home/zx2c4/example + [+] Opened /home/zx2c4/example.ct at /home/zx2c4/example + zx2c4@thinkpad ~ $ echo "super secret" > example/mysecretfile.txt + zx2c4@thinkpad ~ $ ctmg close example + [#] umount /home/zx2c4/example + [#] cryptsetup luksClose ct_example + [#] rmdir /home/zx2c4/example + [+] Closed /home/zx2c4/example.ct + +### Installation + + # make install + +Or, use the package from your distribution: + +#### Gentoo + + # emerge ctmg + +### Bug reports + +Report any bugs to <jason@zx2c4.com>. @@ -0,0 +1,154 @@ +#!/bin/bash + +# Copyright (c) 2015 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. +# Copyright (c) 2014 Laurent Ghigonis <laurent@gouloum.fr>. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +CT_FILE_SUFFIX=".ct" +CT_MAPPER_PREFIX="ct_" + +trace() { + echo "[#] $@" + "$@" +} + +die() { + echo "[!] $*" >&2 + exit 1 +} + +yesno() { + [[ -t 0 ]] || return 0 + local response + read -r -p "$1 [y/N] " response + [[ $response == [yY] ]] || exit 1 +} + +unwind() { + [[ $keep_open -eq 1 ]] && return + + for i in {1..5}; do + echo -e "$(cut -d ' ' -f 2 /proc/mounts)" | fgrep -wq "$mount_path" || break + trace umount "$mount_path" && break + trace sleep $i + done + + for i in {1..5}; do + trace cryptsetup luksClose "$mapper_name" + [[ $? -eq 0 || $? -eq 4 ]] && break + trace sleep $i + done + + for i in {1..5}; do + [[ ! -d $mount_path ]] && break + trace rmdir "$mount_path" && break + trace sleep $i + done + + keep_open=1 + exit +} + +initialize_container() { + # container_dir = /home/myuser/ + container_dir="$(readlink -f "$(dirname "$1")")" + # container_path = /home/myuser/bla.ct + container_path="$container_dir/$(basename "$1" "$CT_FILE_SUFFIX")$CT_FILE_SUFFIX" + # mount_path = /home/myuser/bla/ + mount_path="$(readlink -f "$container_dir/$(basename "$container_path" "$CT_FILE_SUFFIX")")" + # mapper_name = ct_home-myuser-bla + mapper_name="$CT_MAPPER_PREFIX$(echo -n "${mount_path:1}" | tr -C '[:graph:]' '_' | tr '/' '-')" + # mapper_path = /dev/mapper/ct_home-myuser-bla + mapper_path="/dev/mapper/$mapper_name" + + trap unwind INT TERM EXIT +} + +cmd_usage() { + cat <<-_EOF + Usage: $PROGRAM [ new | delete | open | close | list ] [arguments...] + $PROGRAM new container_path container_size[units_suffix] + $PROGRAM delete container_path + $PROGRAM open container_path + $PROGRAM close container_path + $PROGRAM list + _EOF +} + +cmd_new() { + [[ $# -ne 2 ]] && die "Usage: $PROGRAM new container_path container_size[units_suffix]" + initialize_container "$1" + local container_size="$2" + [[ -e $mapper_path ]] && { keep_open=1; die "$container_path is already open"; } + [[ -e $container_path ]] && yesno "$container_path already exists. Are you sure you want to continue?" + rm -f "$container_path" + trace truncate -s "$container_size" "$container_path" || { trace rm -f "$container_path"; die "Could not create $container_path"; } + trace cryptsetup --cipher aes-xts-plain64 --key-size 512 --hash sha512 --iter-time 5000 --batch-mode luksFormat "$container_path" || { trace rm -f "$container_path"; die "Could not create LUKS volume on $container_path"; } + trace chown ${SUDO_UID:-0}:${SUDO_GID:-0} "$container_path" || { trace rm -f "$container_path"; die "Could not set ownership of $container_path"; } + trace cryptsetup luksOpen "$container_path" "$mapper_name" || { trace rm -f "$container_path"; die "Could not open LUKS volume at $container_path"; } + trace mkfs.ext4 -q -E root_owner=${SUDO_UID:-0}:${SUDO_GID:-0} "$mapper_path" || { trace rm -f "$container_path"; die "Could not format ext4 on the LUKS volume at $container_path"; } + echo "[+] Created new encrypted container at $container_path" +} + +cmd_open() { + [[ $# -ne 1 ]] && die "Usage: $PROGRAM open container_path" + initialize_container "$1" + [[ -f $container_path ]] || { keep_open=1; die "$container_path does not exist or is not a regular file"; } + [[ -e $mapper_path ]] && { keep_open=1; die "$container_path is already open"; } + trace cryptsetup luksOpen "$container_path" "$mapper_name" || die "Could not open LUKS volume at $container_path" + trace mkdir -p "$mount_path" || die "Could not create $mount_path directory" + trace mount "$mapper_path" "$mount_path" || die "Could not mount $container_path to $mount_path" + keep_open=1 + echo "[+] Opened $container_path at $mount_path" +} + +cmd_close() { + [[ $# -ne 1 ]] && die "Usage: $PROGRAM close container_path" + initialize_container "$1" + keep_open=1 + echo -e "$(cut -d ' ' -f 2 /proc/mounts)" | fgrep -wq "$mount_path" && { trace umount "$mount_path" || die "Could not unmount $mount_path"; } + trace cryptsetup luksClose "$mapper_name" + [[ $? -eq 0 || $? -eq 4 ]] || die "Could not close LUKS mapping $mapper_name" + [[ -d $mount_path ]] && { trace rmdir "$mount_path" || echo "[-] Non-fatal: could not remove $mount_path directory" >&2; } + echo "[+] Closed $container_path" +} + +cmd_delete() { + [[ $# -ne 1 ]] && die "Usage: $PROGRAM delete container_path" + yesno "Are you sure you want to delete $1?" + cmd_close "$@" + rm "$container_path" || die "Could not delete $container_path" + echo "[+] Deleted $container_path" +} + +cmd_list() { + [[ $# -ne 0 ]] && die "Usage: $PROGRAM list" + local mount_points="$(sed -n "s:^/dev/mapper/${CT_MAPPER_PREFIX}[^ ]* \\([^ ]\\+\\).*:\\1:p" /proc/mounts)" + [[ -n $mount_points ]] && echo -e "$mount_points" +} + +PROGRAM="$(basename "$0")" + +[[ $UID != 0 ]] && exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " "$(readlink -f "$0")" "$@" + +case "$1" in + h|help|-h|--help) shift; cmd_usage "$@" ;; + n|new|create) shift; cmd_new "$@" ;; + d|del|delete) shift; cmd_delete "$@" ;; + c|close) shift; cmd_close "$@" ;; + l|list) shift; cmd_list "$@" ;; + o|open) shift; cmd_open "$@" ;; + *) cmd_open "$@" ;; +esac +exit 0 |