cryptonas/bin/cbox-manage.sh

477 lines
13 KiB
Bash
Executable File

#!/bin/sh
#
# Copyright (c) 02005 sense.lab <senselab@systemausfall.org>
#
# License: This script is distributed under the terms of version 2
# of the GNU GPL. See the LICENSE file included with the package.
#
# $Id$
#
# this script does EVERYTHING
# all other scripts are only frontends :)
#
# called by:
# - some rc-scripts
# - the web frontend cgi
#
# TODO: check permissions and owners of config files, directories and scripts before
# running cbox-root-actions.sh
set -eu
# default location of config file
CONF_FILE=/etc/cryptobox/cryptobox.conf
LIB_DIR=$(dirname "$0")
# to determine a nice default partition name
DEVICE_NAME_PREFIX="Disk #"
# read the default setting file, if it exists
[ -e /etc/default/cryptobox ] && . /etc/default/cryptobox
[ ! -e "$CONF_FILE" ] && echo "Could not find the configuration file: $CONF_FILE" >&2 && exit 1
# parse config file
. "$CONF_FILE"
[ ! -e "$CONF_FILE" ] && echo "Could not find the distribution specific configuration file: $CONF_FILE" >&2 && exit 1
# parse the distribution specific file
. "$DISTRIBUTION_CONF"
# check for writable log file
[ -w "$LOG_FILE" ] || LOG_FILE=/tmp/$(basename "$LOG_FILE")
# retrieve configuration directory
CONFIG_DIR="$(getent passwd $CRYPTOBOX_USER | cut -d ':' -f 6)/config"
CONFIG_MARKER=cryptobox.marker
## configuration
ROOT_PERM_SCRIPT="$LIB_DIR/cryptobox_root_wrapper"
# ROOT_PERM_SCRIPT needs the MNT_PARENT setting
export MNT_PARENT="$(cd ~; pwd)/mnt"
######## stuff ##########
# all partitions with a trailing number
ALL_PARTITIONS=$(cat /proc/partitions | sed '1,2d; s/ */ /g; s/^ *//' | cut -d " " -f 4 | grep '[0-9]$')
#########################
function log_msg()
{
# the log file is (maybe) not writable during boot - try
# before writing ...
[ -w "$LOG_FILE" ] || return 0
echo >>"$LOG_FILE"
echo "##### `date` #####" >>"$LOG_FILE"
echo "$1" >>"$LOG_FILE"
}
function error_msg()
# parameters: ExitCode ErrorMessage
{
local all=$@
test $# -ne 2 && error_msg 1 "*** invalid call of error_msg *** $all"
echo "[`date`] - $2" | tee -a "$LOG_FILE" >&2
# print the execution stack - not usable with busybox
# caller | sed 's/^/\t/' >&2
exit "$1"
}
# Parameter: device
function is_device_allowed() {
# check for invalid characters and exit if one is found
local device=$(echo "$1" | sed 's#[^a-zA-Z0-9_\-\./]##g')
test "$1" = "$device" || return 1
# remove leading "/dev/"
device=$(echo "$device" | sed 's#^/dev/##')
# return for empty name
test -z "$device" && return 1
for a in $ALL_PARTITIONS
do echo "$device" | grep -q "^$a.*" && return 0
done
# no matching device found - exit with error
return 1
}
function config_set_value()
# parameters: SettingName [SettingValue]
# read from stdin if SettingValue is not defined
{
if [ $# -gt 1 ]
then echo "$2" > "$CONFIG_DIR/$1"
else cat - >"$CONFIG_DIR/$1"
fi
}
function config_get_value()
# parameters: SettingName
{
# use mounted config, if it exists - otherwise use defaults
local conf_dir
test -z "$1" && error_msg 1 "empty setting name"
# check for existence - maybe use default values (even for old
# releases that did not contain this setting)
if test -e "$CONFIG_DIR/$1"
then cat "$CONFIG_DIR/$1"
elif test -e "$CONFIG_DEFAULTS_DIR/$1"
then cat "$CONFIG_DEFAULTS_DIR/$1"
else case "$1" in
# you may place default values for older versions here
# for compatibility
* )
error_msg 2 "unknown configuration value ($1)"
;;
esac
fi
return 0
}
function list_partitions_of_type()
# parameter: { config | crypto | plaindata | unused }
{
local config=
local crypto=
local plaindata=
local unused=
for a in $ALL_PARTITIONS
do if "$ROOT_PERM_SCRIPT" is_crypto_partition "/dev/$a"
then crypto="$crypto /dev/$a"
elif "$ROOT_PERM_SCRIPT" is_config_partition "/dev/$a"
then config="$config /dev/$a"
elif "$ROOT_PERM_SCRIPT" is_plaindata_partition "/dev/$a"
then plaindata="$plaindata /dev/$a"
else unused="$unused /dev/$a"
fi
done
case "$1" in
config )
echo "$config"
;;
crypto )
echo "$crypto"
;;
plaindata )
echo "$plaindata"
;;
unused )
echo "$unused"
;;
* )
error_msg 11 "wrong parameter ($1) for list_partition_types in $(basename $0)"
;;
esac | tr " " "\n" | grep -v '^$'
return 0
}
# Parameter: DEVICE
function get_device_mnt_name() {
"$ROOT_PERM_SCRIPT" get_device_mnt_name "$1"
}
# Parameter: DEVICE
function get_device_uuid() {
"$ROOT_PERM_SCRIPT" get_device_uuid "$1"
}
# Parameter: DEVICE
# return the readable name of the crypto container, if it is already defined
# if undefined - return the uuid
function get_device_name() {
local uuid=$(get_device_uuid "$1")
local dbname=$(config_get_value "names.db" | grep "^$uuid:" | cut -d ":" -f 2-)
# return dbname if it exists
test -n "$dbname" && echo "$dbname" && return 0
# find a nice name for the new partition
local counter=1
local test_name
local test_uuid
local test_result
# try to find a name with the defined "prefix" followed by a number ...
while true
do test_name="$DEVICE_NAME_PREFIX$counter"
if config_get_value "names.db" | grep -q ":$test_name$"
then counter=$((counter+1))
else # save it for next time
set_device_name "$1" "$test_name"
echo "$test_name"
return 0
fi
done
}
function set_device_name()
# TODO: the implementation is quite ugly, but it works (tm)
# Parameter: DEVICE NAME
{
local uuid=$(get_device_uuid "$1")
# remove the old setting for this device and every possible entry with the same name
local new_config=$(config_get_value 'names.db' | sed "/^$uuid:/d; /^[^:]*:$2$/d"; echo "$uuid:$2")
echo "$new_config" | config_set_value "names.db"
}
function does_crypto_name_exist()
# Parameter: NAME
{
config_get_value 'names.db' | grep -q "^[^:]*:$1$"
}
function create_crypto()
# Parameter: DEVICE NAME KEYFILE
# keyfile is necessary, to allow background execution via 'at'
{
local device=$1
local name=$2
local keyfile=$3
# otherwise the web interface will hang
# passphrase may be passed via command line
local key=$(<"$keyfile")
# remove the passphrase-file as soon as possible
dd if=/dev/zero of="$keyfile" bs=512 count=1 2>/dev/null
rm "$keyfile"
log_msg "Creating crypto partition with the cipher $DEFAULT_CIPHER on $device"
echo "$key" | "$ROOT_PERM_SCRIPT" create_crypto "$device"
set_crypto_name "$device" "$name"
}
function is_config_active() {
test -f "$CONFIG_DIR/$CONFIG_MARKER"
}
# Parameter: DEVICE
function is_mounted() {
local name=$(get_device_mnt_name "$1")
test -n "$name" && mountpoint -q "$MNT_PARENT/$name"
}
# Parameter: DEVICE
function is_plain() {
"$ROOT_PERM_SCRIPT" is_plain_partition "$1"
}
# Parameter: DEVICE
function is_encrypted() {
"$ROOT_PERM_SCRIPT" is_crypto_partition "$1"
}
function get_available_disks()
# looks which allowed disks are at the moment connected with the cbox
{
for scan in $SCAN_DEVICES
do for avail in $ALL_PARTITIONS
do echo "$avail" | grep -q "^$scan[0-9]*" && echo "/dev/$avail"
done
done
return 0
}
function mount_crypto()
# Parameter: DEVICE
{
local device=$1
[ -z "$device" ] && error_msg 4 'No valid harddisk found!'
is_mounted "$device" && echo "The crypto filesystem is already active!" && return
# passphrase is read from stdin
log_msg "Mounting a crypto partition from $device"
"$ROOT_PERM_SCRIPT" mount "$device" >>"$LOG_FILE" 2>&1
}
function umount_partition() {
# Parameter: device
local container=$(get_device_name "$1")
"$ROOT_PERM_SCRIPT" umount "$1"
}
function box_purge()
# removing just the first bytes from the harddisk should be enough
# every harddisk will be overriden!
# this feature is only useful for validation
{
# TODO: not ALL harddisks, please!
get_available_disks | while read a
do log_msg "Purging $a ..."
"$ROOT_PERM_SCRIPT" trash_device "$a"
done
}
function turn_off_all_containers() {
# TODO - needs to be implemented
return 0
}
### main ###
# set PATH because thttpd removes /sbin and /usr/sbin for cgis
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
ACTION=help
[ $# -gt 0 ] && ACTION=$1 && shift
case "$ACTION" in
crypto-up )
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'crypto-up'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
mount_crypto "$1"
;;
crypto-down )
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'crypto-down'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
umount_partition "$1"
;;
init )
init_cryptobox </dev/null >>"$LOG_FILE" 2>&1
;;
list_container )
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'list_container'"
case "$1" in
config | unused | plaindata | crypto )
list_partitions_of_type "$1"
;;
* )
return 1
;;
esac
return 0
;;
get_device_name )
# Parameter: DEVICE
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'get_device_name'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
get_device_name "$1"
;;
set_device_name )
# Parameter: DEVICE NAME
[ $# -ne 2 ] && error_msg 10 "invalid number of parameters for 'set_device_name'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
set_device_name "$1" "$2"
;;
device_init )
# Parameter: DEVICE [KEYFILE]
test $# -lt 1 && error_msg 10 "invalid number of parameters for 'device_init' ($@)"
test $# -gt 2 && error_msg 10 "invalid number of parameters for 'device_init' ($@)"
if test $# -eq 2
then test -z "$2" -o ! -e "$2" && error_msg 11 "invalid keyfile ($2) given for 'device_init'"
fi
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
if test $# -eq 2
then "$ROOT_PERM_SCRIPT" create_crypto "$1" "$2"
else "$ROOT_PERM_SCRIPT" create_plain "$1"
fi
true
;;
is_mounted )
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'is_mounted'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
is_mounted "$1"
;;
is_encrypted )
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'is_encrypted'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
is_encrypted "$1"
;;
is_plain )
[ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'is_plain'"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
is_plain "$1"
;;
check_config)
is_config_active
;;
get_available_disks )
get_available_disks
;;
set_config )
[ $# -ne 2 ] && error_msg 7 "'set_config' requires two parameters"
config_set_value "$1" "$2"
;;
get_config )
[ $# -ne 1 ] && error_msg 6 "'get_config' requires exactly one parameter"
config_get_value "$1"
;;
get_capacity_info )
[ $# -ne 1 ] && error_msg 6 "'get_capacity_info' requires exactly one parameter"
is_device_allowed "$1" || error_msg 12 "invalid device: $1"
is_mounted "$1" || error_msg 13 "the device is not mounted: $1"
name=$(get_device_mnt_name "$1")
df -h "$MNT_PARENT/$name" | tail -1
;;
diskinfo )
get_available_disks | while read a
do "$ROOT_PERM_SCRIPT" diskinfo "$a"
done 2>/dev/null
;;
box-purge )
log_msg "Cleaning the CryptoBox ..."
turn_off_all_containers
"$0" config-down
box_purge >>"$LOG_FILE" 2>&1
;;
poweroff )
log_msg "Shutting down the Cryptobox ..."
turn_off_all_containers
"$ROOT_PERM_SCRIPT" poweroff
;;
reboot )
log_msg "Rebooting the Cryptobox ..."
turn_off_all_containers
"$ROOT_PERM_SCRIPT" reboot
;;
umount_all )
log_msg "Unmounting all volumes ..."
turn_off_all_containers
;;
* )
echo "[$(basename $0)] - unknown action: $ACTION" >&2
echo "Syntax: $(basename $0) ACTION [PARAMS]"
echo " crypto-up - mount crypto partition"
echo " crypto-down - unmount crypto partition"
echo " crypto-create - a wrapper for 'crypto-create-bg'"
echo " crypto-create-bg - create encrypted blockdevice and run mkfs"
echo " is_mounted - check, if crypto partition is mounted"
echo " check_config - check, if the configuration is usable"
echo " get_available_disks - shows all accessible disks"
echo " get_current_ip - get the current IP of the network interface"
echo " set_config NAME VALUE - change a configuration setting"
echo " get_config NAME - retrieve a configuration setting"
echo " get_device_name DEVICE - retrieve the human readable name of a partition"
echo " set_device_name DEVICE - set the human readable name of a partition"
echo " device_init DEVICE KEYFILE - initialize the filesystem of a partition (the keyfile just contains the passphrase)"
echo " get_capacity_info - print the output of 'df' for the (mounted) partition"
echo " diskinfo - show the partition table of the harddisk"
echo " box-purge - destroy the partition tables of all harddisks (delete everything)"
echo " poweroff - turn off the computer"
echo " reboot - reboot the computer"
echo
;;
esac
exit 0