diff --git a/bin/cbox-manage.sh b/bin/cbox-manage.sh index d4de970..5abf15b 100755 --- a/bin/cbox-manage.sh +++ b/bin/cbox-manage.sh @@ -20,24 +20,24 @@ set -eu -# define reasonable defaults -USE_STUNNEL=0 -EXEC_FIREWALL_RULES=0 -SKIP_NETWORK_CONFIG=1 + +# default location of config file CONF_FILE=/etc/cryptobox/cryptobox.conf LIB_DIR=$(dirname "$0") -CONFIG_DIR=~/config + +# 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" ] && error_msg 1 "Could not find the configuration file: $CONF_FILE" +[ ! -e "$CONF_FILE" ] && echo "Could not find the configuration file: $CONF_FILE" >&2 && exit 1 # parse config file source "$CONF_FILE" -[ ! -e "$CONF_FILE" ] && error_msg 1 "Could not find the distribution specific configuration 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 source "$DISTRIBUTION_CONF" @@ -45,21 +45,26 @@ source "$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 -CERT_TEMP=/tmp/stunnel.pem -ROOT_PERM_SCRIPT=$(dirname "$0")/cryptobox_root_wrapper +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=$(cat /proc/partitions | sed '1,2d; s/ */ /g; s/^ *//' | cut -d " " -f 4) +# 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 not writable during boot - try before writing ... + # the log file is (maybe) not writable during boot - try + # before writing ... [ -w "$LOG_FILE" ] || return 0 echo >>"$LOG_FILE" echo "##### `date` #####" >>"$LOG_FILE" @@ -70,6 +75,8 @@ function log_msg() 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 @@ -77,16 +84,30 @@ function error_msg() } +# 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 { - [ "$USE_SEPERATE_CONFIG_PARTITION" = "1" ] && "$ROOT_PERM_SCRIPT" remount_config rw if [ $# -gt 1 ] - then echo -n "$2" > "$CONFIG_DIR/$1" + then echo "$2" > "$CONFIG_DIR/$1" else cat - >"$CONFIG_DIR/$1" fi - [ "$USE_SEPERATE_CONFIG_PARTITION" = "1" ] && "$ROOT_PERM_SCRIPT" remount_config ro } @@ -95,66 +116,22 @@ function config_get_value() { # use mounted config, if it exists - otherwise use defaults local conf_dir - if is_config_active - then conf_dir=$CONFIG_DIR - else conf_dir=$CONFIG_DEFAULTS_DIR - fi - [ -z "$1" ] && error_msg 1 "empty setting name" - # check for existence - maybe use default values (for old releases without this setting) - if [ ! -e "$conf_dir/$1" ] - then case "$1" in + 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)" - # empty output ;; esac - else echo -n $(cat "$conf_dir/$1") - # this removes the trailing line break - fi - # always return without error - true -} - - -function create_config() -# Parameter: device -{ - local device=$1 - unload_config - # create the new configuration filesystem if it is not static - if [ "$USE_SEPERATE_CONFIG_PARTITION" != "1" ] - then log_msg "Using static configuration ..." - else log_msg "Creating config filesystem ..." - "$ROOT_PERM_SCRIPT" create_config "$device" - log_msg "Mounting config partition ..." - "$ROOT_PERM_SCRIPT" mount_config "$device" - "$ROOT_PERM_SCRIPT" remount_config rw - fi - log_msg "Copying configuration defaults ..." - cp -a "$CONFIG_DEFAULTS_DIR/." "$CONFIG_DIR" - - log_msg "Copying temporary certificate file to config filesystem ..." - # beware: the temp file should always be there - even after reboot - see "load_config" - [ "$USE_STUNNEL" = 1 ] && cp -p "$CERT_TEMP" "$CERT_FILE" - - log_msg "Setting inital values ..." - # beware: config_set_value remounts the config partition read-only - config_set_value "ip" "$(get_current_ip)" - # create database of readable names - config_set_value "names.db" "" - # create a marker to recognize a cryptobox partition - # this should be the last step, to prevent a half-initialized state - config_set_value "$CONFIG_MARKER" "$(date -I)" -} - - -function get_current_ip() -# not necessarily the same as configured (necessary for validation) -{ - # filter the output of ifconfig and remove trailing line break - echo -n $("$IFCONFIG" "$NET_IFACE" | grep "inet" | cut -d ":" -f2 | cut -d " " -f1) + fi + return 0 } @@ -176,45 +153,73 @@ function list_partitions_of_type() fi done case "$1" in - config | crypto | plaindata | unused ) - # dirty hack, but it works - eval "echo \$$1" + config ) + echo "$config" + ;; + crypto ) + echo "$crypto" + ;; + plaindata ) + echo "$plaindata" + ;; + unused ) + echo "$unused" ;; * ) - errot_msg "wrong parameter ($1) for list_partition_types in $(basename $0)" + error_msg 11 "wrong parameter ($1) for list_partition_types in $(basename $0)" ;; esac | tr " " "\n" | grep -v '^$' + return 0 } -function get_crypto_uuid() # Parameter: DEVICE -{ - "$ROOT_PERM_SCRIPT" get_device_name "$1" +function get_device_mnt_name() { + "$ROOT_PERM_SCRIPT" get_device_mnt_name "$1" } -function get_crypto_name() # Parameter: DEVICE -# return the readable name of the crypto container, it it is already defined +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 -{ - local uuid=$(get_crypto_uuid "$1") +function get_device_name() { + local uuid=$(get_device_uuid "$1") local dbname=$(config_get_value "names.db" | grep "^$uuid:" | cut -d ":" -f 2-) - if [ -z "$dbname" ] - then echo -n "$uuid" - else echo -n "$dbname" - fi + # 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_crypto_name() +function set_device_name() # TODO: the implementation is quite ugly, but it works (tm) # Parameter: DEVICE NAME { - local uuid=$(get_crypto_uuid "$1") + local uuid=$(get_device_uuid "$1") # remove the old setting for this device and every possible entry with the same name - (config_get_value 'names.db' | sed "/^$uuid:/d; /^[^:]*:$2$/d"; echo "$uuid:$2") | config_set_value 'names.db' + local new_config=$(config_get_value 'names.db' | sed "/^$uuid:/d; /^[^:]*:$2$/d"; echo "$uuid:$2") + echo "$new_config" | config_set_value "names.db" } @@ -252,45 +257,11 @@ function is_config_active() } -function is_crypto_mounted() +function is_mounted() # Parameter: DEVICE { - local name=$(get_crypto_uuid "$1") - [ -n "$name" ] && mountpoint -q "$MNT_PARENT/$name" -} - - -function is_init_running() -{ - check_at_command_queue " init" -} - - -# check if a specified command is in an at-queue -# Parameter: a regular expression of the commandline -# Return: the command is part of an at-queue (0) or not (1) -function check_at_command_queue() -{ - # 1) get the available job numbers - # 2) remove empty lines (especially the last one) - # 3) check every associated command for the regexp - at -l | cut -f 1 | while read jobnum - do at -c $jobnum | sed '/^$/d' | tail -1 - done | grep -q "$1" -} - - -function find_harddisk() -# look for the harddisk to be partitioned -{ - local device=$(get_available_disks | head -1) - if [ -z "$device" ] ; then - log_msg "no valid harddisk for initialisation found!" - cat /proc/partitions >>"$LOG_FILE" - # do not return with an error, to avoid a failing of the script ('break on error') - # the caller of this function should handle an empty return string - fi - echo -n "$device" + local name=$(get_device_mnt_name "$1") + test -n "$name" && mountpoint -q "$MNT_PARENT/$name" } @@ -299,67 +270,29 @@ function get_available_disks() { for scan in $SCAN_DEVICES do for avail in $ALL_PARTITIONS - do [ "$scan" = "$avail" ] && echo "/dev/$avail" + do echo "$avail" | grep -q "^$scan[0-9]*" && echo "/dev/$avail" done done return 0 } -function load_config() -{ - unload_config - local status=0 - # look for a configuration partition - [ "$USE_SEPERATE_CONFIG_PARTITION" = "1" ] && \ - list_partitions_of_type config | while read part && [ "$status" = 0 ] - do log_msg "Trying to load configuration from /dev/$part ..." - if "$ROOT_PERM_SCRIPT" is_config_partition "/dev/$part" - then log_msg "configuraton found on $part" - "$ROOT_PERM_SCRIPT" mount_config "/dev/$part" - status=1 - fi - done - if is_config_active - then # copy certificate to /tmp in case of re-initialization - # /tmp should be writable, so tmpfs has to be mounted before (/etc/rcS.d) - [ "$USE_STUNNEL" = 1 ] && cp "$CERT_FILE" "$CERT_TEMP" - else log_msg "failed to locate config partition" - return 1 - fi - true -} - - -function unload_config() -{ - is_config_active || return - # only try to unmount, if it is not static (the config of a live-cd is always dynamic) - if [ "$USE_SEPERATE_CONFIG_PARTITION" = "1" ] - then "$ROOT_PERM_SCRIPT" umount_config - else true - fi -} - - function mount_crypto() # Parameter: DEVICE { local device=$1 - [ -z "$device" ] && error_msg 4 'No valid harddisk found!' && return 1 - is_crypto_mounted "$device" && echo "The crypto filesystem is already active!" && return + [ -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_crypto() -# Parameter: DEVICE -{ - local device=$1 - local uuid=$(get_crypto_uuid $device) - "$ROOT_PERM_SCRIPT" umount "$uuid" +function umount_partition() { +# Parameter: device + local container=$(get_device_name "$1") + "$ROOT_PERM_SCRIPT" umount "$1" } @@ -375,27 +308,9 @@ function box_purge() done } - -function init_cryptobox() -# this is only the first part of initialisation that takes no time - good for a smooth web interface -{ - local device=$(find_harddisk) - [ -z "$device" ] && log_msg 'No valid harddisk found!' && return 1 - turn_off_all_crypto - unload_config || true - log_msg "Partitioning the device ($device) ..." - "$ROOT_PERM_SCRIPT" partition_disk "$device" "0,1,L \n,,L\n" - log_msg "Initializing config partition on ${device}1 ..." - # TODO: this should not be hard-coded - create_config "${device}1" -} - -function turn_off_all_crypto() -{ - list_crypto_containers | while read a - do is_crypto_mounted "$a" && umount_crypto "$a" - done +function turn_off_all_containers() { + "$ROOT_PERM_SCRIPT" create_plain "$1" } @@ -409,137 +324,68 @@ ACTION=help [ $# -gt 0 ] && ACTION=$1 && shift case "$ACTION" in - config-up ) - if load_config - then echo "Cryptobox configuration successfully loaded" - else error_msg 0 "Could not find a configuration partition!" - fi - ;; - config-down ) - unload_config || error_msg 4 "Could not unmount configuration partition" - ;; - network-up ) - if [ "$SKIP_NETWORK_CONFIG" != 1 ] - then conf_ip=$(config_get_value "ip") - log_msg "Configuring $NET_IFACE for $conf_ip ..." - echo "Configuring network interface for $NET_IFACE: $conf_ip" - "$IFCONFIG" "$NET_IFACE" "$conf_ip" - fi - if [ "$EXEC_FIREWALL_RULES" = 1 ] - then log_msg "Starting the firewall ..." - "$FIREWALL_SCRIPT" start - fi - if [ "$USE_STUNNEL" = 1 ] - then # start stunnel - if [ -f "$CERT_FILE" ] - then USE_CERT=$CERT_FILE - else USE_CERT=$CERT_TEMP - $MAKE_CERT_SCRIPT "$CERT_TEMP" >>"$LOG_FILE" 2>&1 - # TODO: this could be dangerous - right? - # this is necessary, to allow www-data to copy the certificate - chown "$WEB_USER" "$CERT_TEMP" - fi - log_msg "Starting stunnel ..." - stunnel -p "$USE_CERT" -r localhost:80 -d 443 \ - || echo "$USE_CERT not found - not starting stunnel" - fi - ;; - network-down ) - if [ "$EXEC_FIREWALL_RULES" = 1 ] - then log_msg "Stopping the firewall ..." - "$FIREWALL_SCRIPT" stop - fi - if [ "$USE_STUNNEL" = 1 ] - then log_msg "Stopping stunnel ..." - # TODO: what about a pid? - killall stunnel 2>/dev/null || true - fi - if [ "$SKIP_NETWORK_CONFIG" != 1 ] - then log_msg "Shutting the network interface down ..." - "$IFCONFIG" "$NET_IFACE" down - fi - ;; - services-up ) - # the mount point has to be writeable - # this action is called as root - so we are allowed to umount - # TODO: do this only for ro-filesystem - # TODO: this way of mounting is evil - if mountpoint -q "$MNT_PARENT" - then true - else mount -t tmpfs tmpfs "$MNT_PARENT" - fi - true - ;; - services-down ) - # this action is called as root - so we are allowed to umount - mountpoint -q "$MNT_PARENT" && umount "$MNT_PARENT" - # TODO: we should not depend on samba and thttpd - # /etc/init.d/samba stop || true - # /etc/init.d/thttpd stop || true - true - ;; crypto-up ) - [ $# -ne 1 ] && error_msg "invalid number of parameters for '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 "invalid number of parameters for 'crypto-down'" - umount_crypto "$1" + [ $# -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 >"$LOG_FILE" 2>&1 ;; - crypto-create ) - # Parameter: DEVICE NAME - [ $# -ne 2 ] && error_msg "invalid number of parameters for 'crypto-create'" - # do it in the background to provide a smoother web interface - # messages and errors get written to $LOG_FILE - keyfile=/tmp/$(basename "$0")-passphrase-$(basename "$1") - # read the password - cat - >"$keyfile" - # execute it in the background - echo "'$0' crypto-create-bg '$1' '$2' '$keyfile' >'$LOG_FILE' 2>&1" | at now + 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 ;; - crypto-create-bg ) - create_crypto "$@" - ;; - crypto-list ) - list_partitions_of_type crypto - ;; - crypto-list-unused ) - list_partitions_of_type unused - ;; - crypto-name ) + get_device_name ) # Parameter: DEVICE - get_crypto_name "$1" + [ $# -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" ;; - is_crypto_mounted ) - [ $# -ne 1 ] && error_msg 10 "invalid number of parameters for 'is_crypto_mounted'" - is_crypto_mounted "$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" ;; - is_config_mounted ) + 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 TODO "$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" + ;; + check_config) is_config_active ;; - is_init_running ) - is_init_running - ;; - is_harddisk_available ) - [ -z "$(find_harddisk)" ] && exit 1 - exit 0 - ;; - update_ip_address ) - # reconfigure the network interface to a new IP address - # wait for 5 seconds to finish present http requests - if [ "$SKIP_NETWORK_CONFIG" != 1 ] - then echo -n "sleep 5; \"$ROOT_PERM_SCRIPT\" update_network" | at now - fi - ;; get_available_disks ) get_available_disks ;; - get_current_ip ) - get_current_ip - ;; set_config ) [ $# -ne 2 ] && error_msg 7 "'set_config' requires two parameters" config_set_value "$1" "$2" @@ -548,53 +394,59 @@ case "$ACTION" in [ $# -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 + done 2>/dev/null ;; box-purge ) log_msg "Cleaning the CryptoBox ..." - turn_off_all_crypto + turn_off_all_containers "$0" config-down box_purge >>"$LOG_FILE" 2>&1 ;; poweroff ) - log_msg "Turning off the CryptoBox ..." - turn_off_all_crypto - echo "poweroff" | at now + log_msg "Shutting down the Cryptobox ..." + turn_off_all_containers + "$ROOT_PERM_SCRIPT" poweroff ;; reboot ) - log_msg "Rebooting the CryptoBox ..." - turn_off_all_crypto - echo "reboot" | at now + 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 "Syntax: `basename $0` ACTION [PARAMS]" - echo " config-up - scan for configuration partition and mount it" - echo " config-down - unmount configuration partition" - echo " network-up - enable network interface" - echo " network-down - disable network interface" - echo " services-up - run some cryptobox specific daemons" - echo " services-down - stop some cryptobox specific daemons" + 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 " box-init - initialize cryptobox (ALL data is LOST)" - echo " box-init-fg - the first part of initialization" - echo " box-init-bg - the last part of initialization (background)" - echo " is_crypto_mounted - check, if crypto partition is mounted" - echo " is_config_mounted - check, if configuration partition is mounted" - echo " is_init_running - check, if initialization is ongoing" - echo " is_harddisk_available - check, if there is a usable harddisk" - echo " get_available_disks - shows all connected and allowed disks" + 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 " update_ip_address - update the network interface after reconfiguration" 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 partitiontable of all harddisks (delete everything)" - echo " poweroff - shutdown the cryptobox" - echo " reboot - reboot the cryptobox" + 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 diff --git a/bin/cbox-root-actions.sh b/bin/cbox-root-actions.sh index 1501d34..2d7b2db 100755 --- a/bin/cbox-root-actions.sh +++ b/bin/cbox-root-actions.sh @@ -17,6 +17,9 @@ set -eu +LIB_DIR=$(dirname "$0") +LIB_DIR=$(cd "$LIB_DIR"; pwd) + [ "$(id -u)" -ne 0 ] && echo "$(basename $0) - only root may call this script" >&2 && exit 100 # read the default setting file, if it exists @@ -29,6 +32,9 @@ source "$CONF_FILE" # parse distribution specific file source "$DISTRIBUTION_CONF" +CB_SCRIPT="$LIB_DIR/cbox-manage.sh" +CONFIG_MARKER=cryptobox.marker + ############ some useful functions ############### @@ -46,43 +52,54 @@ function is_device_allowed() } -function get_device_name() -# return the uuid of the device -# if there is no uuid, then the device name is "flattened" and returned +# return the uuid of the partition (if possible) +# this only works for luks or ext2/3 partitions # ignore volume-id as it may be non-unique -{ - local UUID= +function get_device_uuid() { + local UUID # check for luksUUID or ext2/3-uuid if is_luks_device "$1" then UUID=$("$CRYPTSETUP" luksUUID "$1") - else [ -n "$(which dumpe2fs)" ] && UUID=$(dumpe2fs -h "$1" 2>/dev/null | grep "UUID" | cut -d ":" -f 2 | sed "s/ *//g") + else [ -x "$DUMPE2FS" ] && UUID=$("$DUMPE2FS" -h "$1" 2>/dev/null | grep "UUID" | cut -d ":" -f 2 | sed "s/ //g") fi - # if there is no valid UUUD, then take the flattened device name - is_uuid_valid "$UUID" || UUID=${1//\//_} - echo "$UUID" + if test -z "$UUID" + then get_device_flat_name "$1" + else echo "$UUID" + fi + return 0 +} + + +# the device name is "flattened" +function get_device_flat_name() { + echo "$1" | sed 's#/#_#g' +} + + +# the basename of the mountpoint for this device - should be somehow human_readable +function get_device_mnt_name() { + "$CB_SCRIPT" get_device_name "$1" } -function is_uuid_valid() # every devmapper name should look like a UUID -{ +function is_uuid_valid() { local hex=[0-9a-f] + # TODO: this is very bash-specific - translate it to grep :) [[ "$1" =~ "^$hex\{8\}-$hex\{4\}-$hex\{4\}-$hex\{4\}-$hex\{12\}$" ]] } -function error_msg() # parameter ExitCode ErrorMessage -{ +function error_msg() { echo "CBOX-ERROR: [$(basename $0) - $ACTION] - $2" >&2 exit $1 } -function partition_device() # parameter: device sfdisk_layout_setup # e.g.: /dev/hda "0,1,L \n,,L\n" -{ +function partition_device() { # TODO: allow different layouts # TODO: skip config partition if a configuration is already active # sfdisk -n doesn't actually write (for testing purpose) @@ -97,7 +114,7 @@ function partition_device() function is_luks_device() # parameter: device { - "$CRYPTSETUP" isLuks "$1" + "$CRYPTSETUP" isLuks "$1" 2>/dev/null } @@ -121,19 +138,19 @@ case "$ACTION" in [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" is_device_allowed "$1" || \ error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" - name=$(get_device_name "$1") - mountpoint -q "$MNT_PARENT/$name" && \ - error_msg 5 "a device with the same name ($name) is already mounted" - mkdir -p "$MNT_PARENT/$name" + mnt_name=$(get_device_mnt_name "$1") + mountpoint -q "$MNT_PARENT/$mnt_name" && \ + error_msg 5 "a device with the same name ($mnt_name) is already mounted" + mkdir -p "$MNT_PARENT/$mnt_name" if is_luks_device "$1" - then "$CRYPTSETUP" luksOpen "$1" "$name" || \ + then "$CRYPTSETUP" luksOpen "$1" "$mnt_name" || \ error_msg 6 "could not open encrypted device $1" - if mount "$DEV_MAPPER_DIR/$name" "$MNT_PARENT/$name" + if mount "$DEV_MAPPER_DIR/$mnt_name" "$MNT_PARENT/$mnt_name" then true - else "$CRYPTSETUP" luksClose "$name" || true + else "$CRYPTSETUP" luksClose "$mnt_name" || true error_msg 7 "wrong password for $1 supplied" fi - else mount "$1" "$MNT_PARENT/$name" || \ + else mount "$1" "$MNT_PARENT/$mnt_name" || \ error_msg 8 "invalid filesystem on device $1" fi # just in case, that there is no ext2/3 filesystem: @@ -145,105 +162,86 @@ case "$ACTION" in true ;; umount ) - #parameter: name (relative mountpoint) + #parameter: device [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" - is_uuid_valid "$1" || [[ "$1" =~ "^[_a-z0-9]*$" ]] || \ - error_msg 4 "invalid UUID ($1)" - mountpoint -q "$MNT_PARENT/$1" || \ - error_msg 9 "the device with the UUID ($1) is not mounted" - # try to unmount - continue even on errors - umount "$MNT_PARENT/$1" || \ - error_msg 0 "unmount of device $1 failed - device is busy" - # remove (if necessary) the dev mapping - [ -e "$DEV_MAPPER_DIR/$1" ] && "$CRYPTSETUP" luksClose "$1" || \ - error_msg 11 "could not remove the device mapper for device $1" + is_device_allowed "$1" || \ + error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" + mnt_name=$(get_device_mnt_name "$1") + mountpoint -q "$MNT_PARENT/$mnt_name" || \ + error_msg 9 "the device ($1) is not mounted as '$mnt_name'" + # try to unmount - do it in lazy mode + umount -l "$MNT_PARENT/$mnt_name" + # TODO: check, what happens, if there are open files - does the device gets mapping removed? + # remove (if necessary) the device mapping + if test -e "$DEV_MAPPER_DIR/$mnt_name" + then "$CRYPTSETUP" luksClose "$mnt_name" || \ + error_msg 11 "could not remove the device mapper ($mnt_name) for device $1" + fi # try to remove the mountpoint - a failure is not important - rmdir "$MNT_PARENT/$1" || true + rmdir "$MNT_PARENT/$mnt_name" || true # set exitcode - mountpoint -q "$MNT_PARENT/$1" && exit 1 - exit 0 + mountpoint -q "$MNT_PARENT/$mnt_name" && exit 1 + true ;; create_crypto ) - # parameter: device - # the passphrase is expected on stdin - [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" + # parameter: device keyfile + [ $# -ne 2 ] && error_msg 1 "wrong number of parameters" + keyfile=$2 + [ -e "$keyfile" ] || error_msg 2 "keyfile ($keyfile) not found" is_device_allowed "$1" || \ error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" # read the passphrase from stdin - key=$(cat -) # the iter-time is in milliseconds - keep it low for fast mounting - echo "$key" | \ - "$CRYPTSETUP" --cipher "$DEFAULT_CIPHER" --iter-time 2000 luksFormat "$1" || \ + cat "$keyfile" | \ + "$CRYPTSETUP" --cipher "$DEFAULT_CIPHER" --iter-time 2000 --batch-mode luksFormat "$1" || \ error_msg 11 "failed to create the encrypted partition" - name=$(get_device_name "$1") - echo "$key" | "$CRYPTSETUP" luksOpen "$1" "$name" || \ + name=$(get_device_mnt_name "$1") + cat "$keyfile" | "$CRYPTSETUP" --batch-mode luksOpen "$1" "$name" || \ error_msg 12 "failed to open the encrypted partition" - # silent output from mkfs.ext3 - "$MKFS_DATA" -q "$DEV_MAPPER_DIR/$name" || \ - error_msg 13 "failed to create the encrypted filesystem" - "$CRYPTSETUP" luksClose "$name" || \ - error_msg 14 "failed to close the encrypted mapped device" - ;; - get_device_name ) - # parameter: device - [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" - is_device_allowed "$1" || \ - error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" - get_device_name "$1" - ;; - mount_config ) - # parameter: device - [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" - [ "$USE_SEPERATE_CONFIG_DIR" != "1" ] && \ - error_msg 19 "I am configured to work without a seperate config partition (see $CONF_FILE)" - is_device_allowed "$1" || \ - error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" - mountpoint -q "$CONFIG_DIR" && \ - error_msg 0 "the configuration partition is already mounted" - "$MKFS_CONFIG" -q "$1" || \ - error_msg 20 "failed to create config partition filesystem" - ;; - mount_config ) - # parameter: device - [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" - [ "$USE_SEPERATE_CONFIG_DIR" != "1" ] && \ - error_msg 19 "I am configured to work without a seperate config partition (see $CONF_FILE)" - is_device_allowed "$1" || \ - error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" - mountpoint -q "$CONFIG_DIR" && \ - error_msg 0 "the configuration partition is already mounted" - mount "$1" "$CONFIG_DIR" || \ - error_msg 16 "failed to mount configuration partition" - # chown to fix permissions - may fail for non-ext2/3 filesystems - chown -R "$WEB_USER" "$CONFIG_DIR" || true - mount -o remount,ro "$CONFIG_DIR" || \ - error_msg 18 "failed to remount configuration partition" + # trash the passphrase in keyfile + echo "0123456789abcdefghijklmnopqrstuvwxyz" > "$keyfile" + # the disk cache surely prevents the previous line from being written, but we do it anyway ... + echo "zyxwvutsrqponmlkjihgfedcba9876543210" > "$keyfile" + rm "$keyfile" + # complete in background + ( # silent output from mkfs.ext3 + "$MKFS_DATA" -q "$DEV_MAPPER_DIR/$name" || \ + error_msg 13 "failed to create the encrypted filesystem" + "$CRYPTSETUP" --batch-mode luksClose "$name" || \ + error_msg 14 "failed to close the encrypted mapped device" + ) /dev/null & true ;; - remount_config ) - # parameter: { ro | rw } - [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" - [ "$USE_SEPERATE_CONFIG_DIR" != "1" ] && \ - error_msg 19 "I am configured to work without a seperate config partition (see $CONF_FILE)" - [[ "$1" =~ "^r[ow]$" ]] || error_msg 17 "only 'rw' and 'ro' are allowed" - mount -o "remount,$1" "$CONFIG_DIR" || \ - error_msg 18 "failed to remount configuration partition" + create_plain ) + # parameter: device + [ $# -ne 1 ] && error_msg 1 "wrong number of parameters for 'create_plain'" + is_device_allowed "$1" || \ + error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" + # complete in background + ( # silent output from mkfs.ext3 + "$MKFS_DATA" -q "$1" || \ + error_msg 15 "failed to create the plaintext filesystem" + ) /dev/null & true ;; - umount_config ) - # no parameters - [ $# -ne 0 ] && error_msg 1 "wrong number of parameters" - [ "$USE_SEPERATE_CONFIG_DIR" != "1" ] && \ - error_msg 19 "I am configured to work without a seperate config partition (see $CONF_FILE)" - mountpoint -q "$CONFIG_DIR" && umount "$CONFIG_DIR" || \ - error_msg 18 "failed to unmount configuration partition" + get_device_mnt_name ) + # parameter: device + [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" + is_device_allowed "$1" || \ + error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" + get_device_mnt_name "$1" + ;; + get_device_uuid ) + # parameter: device + [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" + is_device_allowed "$1" || \ + error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" + get_device_uuid "$1" ;; is_config_partition ) # parameter: device # returns exitcode 0 if the device contains a configuration [ $# -ne 1 ] && error_msg 1 "wrong number of parameters" - [ "$USE_SEPERATE_CONFIG_DIR" != "1" ] && \ - error_msg 19 "I am configured to work without a seperate config partition (see $CONF_FILE)" is_device_allowed "$1" || \ error_msg 3 "this device ($1) is not listed in SCAN_DEVICES (see $CONF_FILE)" is_config=0 @@ -306,10 +304,20 @@ case "$ACTION" in [ -e "$CONFIG_DIR/ip" ] && ip=$(<"$CONFIG_DIR/ip") [ -n "$z" ] && ifconfig "$NET_IFACE" "$ip" ;; + poweroff ) + # TODO: check configuration setting before + "$POWEROFF" + ;; + reboot ) + # TODO: check configuration setting before + "$REBOOT" + ;; * ) + echo "[$(basename $0)] - unknown action: $ACTION" >&2 echo "Syntax: $(basename $0) ACTION PARAMETERS" echo ' partition_disk $device $disk_layout' echo ' get_device_name $device' + echo ' get_device_uuid $device' echo ' create_crypto $device' echo ' mount $device' echo ' umount $name' @@ -323,6 +331,8 @@ case "$ACTION" in echo ' trash_device $device' echo ' diskinfo $device' echo ' update_network' + echo ' poweroff' + echo ' reboot' echo ' help' echo [ "$ACTION" == "help" ] && exit 0 diff --git a/bin/cryptobox.pl b/bin/cryptobox.pl index 738c5c7..86089eb 100755 --- a/bin/cryptobox.pl +++ b/bin/cryptobox.pl @@ -19,24 +19,36 @@ use ClearSilver; use ConfigFile; use English; use CGI::Carp; +use IO::File; +use POSIX; + +use constant CRYPTOBOX_VERSION => 0.3; + +# debug levels +use constant DEBUG_NONE => 0; +use constant DEBUG_ERROR => 1; +use constant DEBUG_WARN => 2; +use constant DEBUG_INFO => 3; # drop privileges $UID = $EUID; $GID = $EGID; +# necessary for suid perl scripts (see 'man perlsec' for details) $ENV{'PATH'} = '/bin:/usr/bin'; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer my $CONFIG_FILE = '/etc/cryptobox/cryptobox.conf'; my $pagedata; my ($LANGUAGE_DIR, $DEFAULT_LANGUAGE, $HTML_TEMPLATE_DIR, $DOC_DIR); -my ($CB_SCRIPT, $LOG_FILE, $IS_DEVEL, $STYLESHEET_URL); +my ($CB_SCRIPT, $LOG_FILE, $IS_DEVEL, $STYLESHEET_URL, $DEBUG_LEVEL); # get the directory of the cryptobox scripts/binaries and untaint it $CB_SCRIPT = $0; $CB_SCRIPT =~ m/^(.*)\/[^\/]*$/; -$CB_SCRIPT = "$1/cbox-manage.sh"; +$CB_SCRIPT = ($1)? "$1/cbox-manage.sh" : './cbox-manage.sh'; &fatal_error ("could not find configuration file ($CONFIG_FILE)") unless (-e $CONFIG_FILE); my $config = ConfigFile::read_config_file($CONFIG_FILE); @@ -48,20 +60,18 @@ $HTML_TEMPLATE_DIR = $config->{HTML_TEMPLATE_DIR}; $DOC_DIR = $config->{DOC_DIR}; $IS_DEVEL = ( -e $config->{DEV_FEATURES_SCRIPT}); $STYLESHEET_URL = $config->{STYLESHEET_URL}; - -# TODO: just a quick-and-dirty hack during migration to multiple containers -my $CRYPTO_DEV = &get_available_disks(); -$CRYPTO_DEV =~ m/^([\w\/_\-\.]*)$/; -$CRYPTO_DEV = "${1}2"; +if (defined($config->{DEBUG_LEVEL})) { + $DEBUG_LEVEL = $config->{DEBUG_LEVEL}; +} else { + $DEBUG_LEVEL = DEBUG_ERROR; # default debug level +} my $query = new CGI; - #################### subs ###################### # for fatal errors without the chance of clearsilver-rendering -sub fatal_error() -{ +sub fatal_error() { my $message = shift; print "Content-Type: text/html\n\n"; @@ -73,8 +83,14 @@ sub fatal_error() } -sub load_hdf -{ +sub debug_msg() { + my ($level, $message) = @_; + return 0 unless ($level >= $DEBUG_LEVEL); + warn "[cryptobox]: $message"; +} + + +sub load_hdf { my $hdf = ClearSilver::HDF->new(); my $fname = "$HTML_TEMPLATE_DIR/main.cs"; @@ -101,8 +117,7 @@ sub load_hdf } -sub load_selected_language -{ +sub load_selected_language { my $data = shift; my $config_language; @@ -110,7 +125,7 @@ sub load_selected_language $data->readFile("$LANGUAGE_DIR/$DEFAULT_LANGUAGE" . ".hdf"); # load configured language, if it is valid - $config_language = &exec_cb_script("get_config","language"); + $config_language = &get_cbox_config("language"); $config_language = $DEFAULT_LANGUAGE unless (&validate_language("$config_language")); # check for preferred browser language, if the box was not initialized yet @@ -130,6 +145,9 @@ sub load_selected_language # load the data $config_language = "$weblang"; # add the setting to every link + # how it should be done now ... + $data->setValue('Settings.LinkAttrs.weblang', "$weblang"); + # old way of doing this ... (TODO: to be removed) $data->setValue('Data.PostData.weblang', "$weblang"); } else { # no valid language was selected - so you may ignore it @@ -153,9 +171,8 @@ sub load_selected_language } -sub get_available_languages # import the names of all available languages -{ +sub get_available_languages { my $data = shift; my ($file, @files, $hdf, $lang_name); @@ -176,8 +193,7 @@ sub get_available_languages # look for preferred browser language setting # this code was adapted from Per Cederberg - http://www.percederberg.net/home/perl/select.perl # it returns an empty string, if no supported language was found -sub get_browser_language -{ +sub get_browser_language { my ($str, @langs, @res); # Use language preference settings @@ -199,8 +215,7 @@ sub get_browser_language } -sub log_msg -{ +sub log_msg { my $text = shift; open(LOGFILE,">> $LOG_FILE"); print LOGFILE "$text"; @@ -208,28 +223,28 @@ sub log_msg } -sub check_ssl -{ +sub check_ssl { # check, if we are behind a proxy with ssl (e.g. pound) return (0==0) if ($ENV{'HTTP_FRONT_END_HTTPS'} =~ m/^on$/i); # environment variable set (e.g. via apache directive "SetEnv HTTPS On") return (0==0) if ($ENV{'HTTPS'} =~ m/^on$/i); # port 80 -> not encrypted - return (0==1) if ($ENV{'SERVER_PORT'} = 80); + return (0==1) if ($ENV{'SERVER_PORT'} == 80); # other ports -> maybe ok - we accept it return (0==0); } -sub check_mounted -{ - return (system($CB_SCRIPT,"is_crypto_mounted",$CRYPTO_DEV) == 0); +# check, if the given device is mounted/used somehow +# Paramter: device +sub check_mounted { + my ($dev) = @_; + return (system($CB_SCRIPT,"is_mounted",$dev) == 0); } -sub check_config -{ - return (system($CB_SCRIPT,"is_config_mounted") == 0); +sub check_config { + return (system($CB_SCRIPT,"check_config") == 0); } @@ -243,50 +258,65 @@ sub exec_cb_script { exit 0; } else { # parent - @result = ; - close PROG_OUT or warn "error while running $CB_SCRIPT (params:" . join(" ",@params) . "): $?"; + # only read lines containing at least one non-whitespace character + @result = grep /\S/, ; + foreach (@result) { chomp; } + unless (close PROG_OUT) { + &debug_msg(DEBUG_WARN, "error while running $CB_SCRIPT (params:" . join(" ",@params) . "): $?"); + return undef; + } } if (wantarray) { return @result; - } else { + } elsif (@result > 0) { return join('',@result); + } else { + return ""; } } -sub check_init_running -{ - return (system($CB_SCRIPT,"is_init_running") == 0); +sub check_init_running { + # TODO: improve this + return (0==1); } -sub is_harddisk_available -{ - return (system($CB_SCRIPT,"is_harddisk_available") == 0); -} - -sub get_available_disks -# TODO: this is useful for diskselection buttons -{ - return &exec_cb_script("get_available_disks"); -} - -sub get_current_ip -# the IP of eth0 - not the configured value of the box (only for validation) -{ - return &exec_cb_script("get_current_ip"); +sub is_harddisk_available { + my @all_disks = &exec_cb_script("get_available_disks"); + return @all_disks > 0; } -sub get_admin_pw -# returns the current administration password - empty, if it is not used -{ - return &exec_cb_script("get_config","admin_pw"); +sub get_available_disks { + my @all_disks = &exec_cb_script("get_available_disks"); + my ($disk, @return_disks); + foreach $disk (@all_disks) { + $disk =~ m#^([/\._\-\w]*)$#; + push @return_disks, $1 if ($1); + } + return @return_disks; } -sub render -{ +sub get_disk_name { + my ($dev) = @_; + my $disk_name = &exec_cb_script("get_device_name", $dev); + return $disk_name; +} + + +# return the value of a configuration setting (timeout, language, ip, ...) +# Parameter: setting_name +sub get_cbox_config { + my ($setting) = @_; + # tell the exec function, that we want a scalar instead of an array + my $scalar = &exec_cb_script("get_config",$setting); + return $scalar; +} + + +sub render { my $pagefile = "$HTML_TEMPLATE_DIR/main.cs"; print "Content-Type: text/html\n\n"; @@ -297,68 +327,82 @@ sub render } -sub mount_vol -{ - my $pw = shift; +# mount an encrypted volume +# Parameter: device password +sub mount_vol { + my ($device, $pw) = @_; - if (&check_mounted) { + if (&check_mounted($device)) { $pagedata->setValue('Data.Warning', 'IsMounted'); } else { - open(PW_INPUT, "| $CB_SCRIPT crypto-up $CRYPTO_DEV"); + open(PW_INPUT, "| $CB_SCRIPT crypto-up $device"); print PW_INPUT $pw; close(PW_INPUT); } } -sub umount_vol -{ - if (&check_mounted) { - system($CB_SCRIPT, "crypto-down",$CRYPTO_DEV); +# unmount a volume +# Parameter: device +sub umount_vol { + my ($device) = @_; + if (&check_mounted($device)) { + system($CB_SCRIPT, "crypto-down",$device); } else { $pagedata->setValue('Data.Warning', 'NotMounted'); } } -sub box_init -{ - my ($crypto_pw, $admin_pw) = @_; +# Parameter: device passphrase +# ignore passphrase (or leave it empty) to create a plaintext volume +sub volume_init { + my ($device, $crypto_pw) = @_; + my $result; - system($CB_SCRIPT, "init") || return 1; - - # partitioning, config and initial cryptsetup - # TODO: define the name of the crypto container - open(PW_INPUT, "|$CB_SCRIPT crypto-create '$CRYPTO_DEV' default"); - print PW_INPUT $crypto_pw; - close(PW_INPUT); - - # set administration password - $admin_pw =~ m/^(.*)$/; - system($CB_SCRIPT, "set_config", "admin_pw", $1); -} - -sub box_purge -{ - system($CB_SCRIPT, "box-purge"); -} - -sub system_poweroff -{ - &umount_vol() if (&check_mounted()); - system($CB_SCRIPT, "poweroff"); + # only for encrypted volumes: + # write passphrase to a file - necessary as perl in secured mode does not allow + # the 'open(FH, "|/bin/prog ....")' call because of possible shell expansion - stupid 'open' :( + if ($crypto_pw) { + my ($fh, $temp_file); + # generate a temporary filename (as suggested by the Perl Cookbook) + do { $temp_file = POSIX::tmpnam() } + # TODO: reduce the file mask to the minimum - maybe 0600 would be a good choice + until $fh = IO::File->new($temp_file, O_RDWR|O_CREAT|O_EXCL); + close $fh; + unless (open(TMP, ">$temp_file")) { + &debug_msg(DEBUG_ERROR, "could not open a temporary file"); + return (1==0); + } + print TMP $crypto_pw; + close TMP; + $result = &exec_cb_script("device_init", $device, $temp_file); + unlink ($temp_file) if (-e $temp_file); + } else { + $result = &exec_cb_script("device_init", $device); + } + # just to be sure, that the file does not get left behind + # usually the script should overwrite and remove it + return defined($result); } -sub system_reboot -{ - &umount_vol() if (&check_mounted()); - system($CB_SCRIPT, "reboot"); +sub box_purge { + &exec_cb_script("box-purge"); } -sub validate_ip -{ +sub system_poweroff { + &exec_cb_script("poweroff"); +} + + +sub system_reboot { + &exec_cb_script("reboot"); +} + + +sub validate_ip { my $ip = shift; my @octets = split /\./, $ip; return 0 if ($#octets == 4); @@ -371,8 +415,7 @@ sub validate_ip } -sub validate_timeout -{ +sub validate_timeout { my $timeout = shift; return 0 if ($timeout =~ /\D/); return 1; @@ -380,8 +423,7 @@ sub validate_timeout # check for a valid interface language -sub validate_language -{ +sub validate_language { my $language = shift; # check for non-alphanumeric character return 0 if ($language =~ /\W/); @@ -392,8 +434,7 @@ sub validate_language # check for a valid documentation language -sub validate_doc_language -{ +sub validate_doc_language { my $language = shift; # check for non-alphanumeric character return 0 if ($language =~ /\W/); @@ -409,6 +450,14 @@ sub validate_doc_language $pagedata = load_hdf(); my $current_admin_pw; +my $action = $query->param('action'); +$action =~ m#^([\w\._\-]*)$#; +$action = ($1)? $1 : ''; + +my $device = $query->param('device'); +$device =~ m#^([/_\-\w\.]*)$#; +$device = ($1)? $1 : ''; + # BEWARE: there are two kinds of actions: # * some require a harddisk # * some do not require a harddisk @@ -423,7 +472,6 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Redirect.URL', "https://" . $hostname . $ENV{'SCRIPT_NAME'}); $pagedata->setValue('Data.Redirect.Delay', "3"); } elsif ($query->param('action')) { - my $action = $query->param('action'); #--------------------------------------------------------------# # here you may define all cases that do not require a harddisk # # put all other cases below the harddisk check # @@ -441,8 +489,8 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Action', 'show_doc'); } ##################### poweroff ###################### - } elsif ($action eq 'shutdown_ask') { - $pagedata->setValue('Data.Action', 'form_shutdown'); + } elsif ($action eq 'system_ask') { + $pagedata->setValue('Data.Action', 'form_system'); ##################### reboot ######################## } elsif ($action eq 'shutdown_do') { if ($query->param('type') eq 'reboot') { @@ -465,7 +513,11 @@ if ( ! &check_ssl()) { #-------------------------------------------------------# ################ umount_do ####################### } elsif ($action eq 'umount_do') { - if ( ! &check_config()) { + if ($device eq '') { + &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); + $pagedata->setValue('Data.Warning', 'InvalidDevice'); + $pagedata->setValue('Data.Action', 'emptu'); + } elsif ( ! &check_config()) { $pagedata->setValue('Data.Warning', 'NotInitialized'); $pagedata->setValue('Data.Action', 'form_init'); } elsif (&check_init_running()) { @@ -473,26 +525,27 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Action', 'empty'); $pagedata->setValue('Data.Redirect.Action', 'form_config'); $pagedata->setValue('Data.Redirect.Delay', "30"); - } elsif ( ! &check_mounted()) { + } elsif ( ! &check_mounted($device)) { $pagedata->setValue('Data.Warning', 'NotMounted'); - $pagedata->setValue('Data.Action', 'empty'); + $pagedata->setValue('Data.Action', 'show_volume'); } else { # unmounten - &umount_vol(); - if (&check_mounted()) { + &umount_vol($device); + if (&check_mounted($device)) { $pagedata->setValue('Data.Warning', 'UmountFailed'); - $pagedata->setValue('Data.Action', 'form_umount'); + $pagedata->setValue('Data.Action', 'show_volume'); } else { - $pagedata->setValue('Data.Success', 'UmountDone'); - $pagedata->setValue('Data.Action', 'empty'); - $pagedata->setValue('Data.Redirect.Action', 'show_status'); - $pagedata->setValue('Data.Redirect.Delay', "30"); + #$pagedata->setValue('Data.Success', 'UmountDone'); + $pagedata->setValue('Data.Action', 'show_volume'); } } ################ mount_do ######################## } elsif ($action eq 'mount_do') { - # mount requested - if ( ! &check_config()) { + if ($device eq '') { + &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); + $pagedata->setValue('Data.Warning', 'InvalidDevice'); + $pagedata->setValue('Data.Action', 'empty'); + } elsif ( ! &check_config()) { $pagedata->setValue('Data.Warning', 'NotInitialized'); $pagedata->setValue('Data.Action', 'form_init'); } elsif (&check_init_running()) { @@ -500,26 +553,22 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Action', 'empty'); $pagedata->setValue('Data.Redirect.Action', 'form_config'); $pagedata->setValue('Data.Redirect.Delay', "30"); - } elsif (&check_mounted()) { + } elsif (&check_mounted($device)) { $pagedata->setValue('Data.Warning', 'IsMounted'); - $pagedata->setValue('Data.Action', 'empty'); - $pagedata->setValue('Data.Redirect.Action', 'show_status'); - $pagedata->setValue('Data.Redirect.Delay', "30"); + $pagedata->setValue('Data.Action', 'show_volume'); } elsif ($query->param('crypto_password') eq '') { # leeres Passwort $pagedata->setValue('Data.Warning', 'EmptyCryptoPassword'); - $pagedata->setValue('Data.Action', 'form_mount'); + $pagedata->setValue('Data.Action', 'show_volume'); } else { # mounten - &mount_vol($query->param('crypto_password')); - if (!&check_mounted()) { + &mount_vol($device, $query->param('crypto_password')); + if (!&check_mounted($device)) { $pagedata->setValue('Data.Warning', 'MountFailed'); - $pagedata->setValue('Data.Action', 'form_mount'); + $pagedata->setValue('Data.Action', 'show_volume'); } else { - $pagedata->setValue('Data.Success', 'MountDone'); - $pagedata->setValue('Data.Action', 'empty'); - $pagedata->setValue('Data.Redirect.Action', 'show_status'); - $pagedata->setValue('Data.Redirect.Delay', "30"); + #$pagedata->setValue('Data.Success', 'MountDone'); + $pagedata->setValue('Data.Action', 'show_volume'); } } ################## mount_ask ####################### @@ -532,11 +581,6 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Action', 'empty'); $pagedata->setValue('Data.Redirect.Action', 'form_config'); $pagedata->setValue('Data.Redirect.Delay', "30"); - } elsif (&check_mounted()) { - $pagedata->setValue('Data.Warning', 'IsMounted'); - $pagedata->setValue('Data.Action', 'empty'); - $pagedata->setValue('Data.Redirect.Action', 'show_status'); - $pagedata->setValue('Data.Redirect.Delay', "30"); } else { $pagedata->setValue('Data.Action', 'form_mount'); } @@ -545,11 +589,6 @@ if ( ! &check_ssl()) { if ( ! &check_config()) { $pagedata->setValue('Data.Warning', 'NotInitialized'); $pagedata->setValue('Data.Action', 'form_init'); - } elsif ( ! &check_mounted()) { - $pagedata->setValue('Data.Warning', 'NotMounted'); - $pagedata->setValue('Data.Action', 'empty'); - $pagedata->setValue('Data.Redirect.Action', 'show_status'); - $pagedata->setValue('Data.Redirect.Delay', "30"); } else { $pagedata->setValue('Data.Action', 'form_umount'); } @@ -566,7 +605,7 @@ if ( ! &check_ssl()) { } #################### init_do ######################## } elsif ($action eq 'init_do') { - $current_admin_pw = &get_admin_pw; + $current_admin_pw = &get_cbox_config("admin_pw"); if ($current_admin_pw ne '' && $current_admin_pw ne $query->param('current_admin_password')) { $pagedata->setValue('Data.Warning', 'WrongAdminPassword'); $pagedata->setValue('Data.Action', 'form_init'); @@ -587,13 +626,11 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Warning', 'InitNotConfirmed'); $pagedata->setValue('Data.Action', 'form_init'); } else { - # do init - &box_init($query->param('crypto_password'),$query->param('admin_password')); - if (!&check_init_running()) { - $pagedata->setValue('Data.Error', 'InitFailed'); - } else { - $pagedata->setValue('Data.Success', 'InitRunning'); + if (&volume_init($query->param('crypto_password'),$query->param('admin_password'))) { + #$pagedata->setValue('Data.Success', 'InitRunning'); $pagedata->setValue('Data.Action', 'form_config'); + } else { + $pagedata->setValue('Data.Error', 'InitFailed'); } } #################### config_ask ###################### @@ -606,34 +643,33 @@ if ( ! &check_ssl()) { } #################### config_do ####################### } elsif ($action eq 'config_do') { + my $query_language = $query->param('language'); + $query_language =~ m/^(\w+)$/; $query_language = $1; + my $query_timeout = $query->param('timeout'); + $query_timeout =~ m/^(\d+)$/; $query_timeout = $1; if ( ! &check_config()) { $pagedata->setValue('Data.Warning', 'NotInitialized'); $pagedata->setValue('Data.Action', 'form_init'); } else { - $current_admin_pw = &get_admin_pw; + $current_admin_pw = &get_cbox_config("admin_pw"); if ($current_admin_pw ne '' && $current_admin_pw ne $query->param('current_admin_password')) { $pagedata->setValue('Data.Warning', 'WrongAdminPassword'); $pagedata->setValue('Data.Action', 'form_config'); - } elsif ( ! &validate_language($query->param('language'))) { + } elsif ( ! &validate_language($query_language)) { $pagedata->setValue('Data.Warning', 'InvalidLanguage'); $pagedata->setValue('Data.Action', 'form_config'); - } elsif ( ! &validate_ip($query->param('ip'))) { - $pagedata->setValue('Data.Warning', 'InvalidIP'); - $pagedata->setValue('Data.Action', 'form_config'); - } elsif ( ! &validate_timeout($query->param('timeout'))) { + } elsif ( ! &validate_timeout($query_timeout)) { $pagedata->setValue('Data.Warning', 'InvalidTimeOut'); $pagedata->setValue('Data.Action', 'form_config'); } else { - system($CB_SCRIPT, "set_config", "language", $query->param('language')); + system($CB_SCRIPT, "set_config", "language", $query_language); &load_selected_language($pagedata); - system($CB_SCRIPT, "set_config", "timeout", $query->param('timeout')); + system($CB_SCRIPT, "set_config", "timeout", $query_timeout); # check, if the ip was reconfigured - if ($query->param('ip') ne &exec_cb_script("get_config","ip")) - { + # TODO: IP stuff should be moved to the live-cd stuff + if (defined($query->param('ip')) && ($query->param('ip') ne &get_cbox_config("ip"))) { # set the new value system($CB_SCRIPT, "set_config", "ip", $query->param('ip')); - # reconfigure the network interface - system($CB_SCRIPT, "update_ip_address"); # redirect to the new address $pagedata->setValue('Data.Redirect.URL', "https://" . $query->param('ip') . $ENV{'SCRIPT_NAME'}); $pagedata->setValue('Data.Redirect.Delay', "5"); @@ -641,20 +677,106 @@ if ( ! &check_ssl()) { $pagedata->setValue('Data.Warning', 'IPAddressChanged'); } # check for success - if (&exec_cb_script("get_config","timeout") ne $query->param('timeout')) { + if (defined($query->param('timeout')) + && (&get_cbox_config("timeout") ne $query->param('timeout'))) { $pagedata->setValue('Data.Warning', 'ConfigTimeOutFailed'); - } elsif (&exec_cb_script("get_config","ip") ne $query->param('ip')) { + } elsif (defined($query->param('ip')) && + (&get_cbox_config("ip") ne $query->param('ip'))) { $pagedata->setValue('Data.Warning', 'ConfigIPFailed'); - } elsif (&exec_cb_script("get_config","language") ne $query->param('language')) { + } elsif (defined($query->param('language')) + && (&get_cbox_config("language") ne $query->param('language'))) { $pagedata->setValue('Data.Warning', 'ConfigLanguageFailed'); } else { - $pagedata->setValue('Data.Success', 'ConfigSaved'); + #$pagedata->setValue('Data.Success', 'ConfigSaved'); } $pagedata->setValue('Data.Action', 'show_status'); $pagedata->setValue('Data.Redirect.Action', 'show_status'); $pagedata->setValue('Data.Redirect.Delay', "30"); } } + ############## change volume name ################### + } elsif ($action eq 'volume_name_set') { + my $volume_name = $query->param('volume_name'); + # remove all special characters which are not white-listed + $volume_name =~ s#[^\w \-_\#/\(\)\[\]]##g; + # untaint variable + $volume_name =~ m#^(.*)$#; $volume_name = $1; + if ($device eq '') { + &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); + $pagedata->setValue('Data.Warning', 'InvalidDevice'); + $pagedata->setValue('Data.Action', 'show_status'); + } elsif (&check_mounted($device)) { + $pagedata->setValue('Data.Warning','VolumeMayNotBeMounted'); + $pagedata->setValue('Data.Action', 'show_volume'); + } elsif ($volume_name eq '') { + $pagedata->setValue('Data.Warning','InvalidVolumeName'); + $pagedata->setValue('Data.Action', 'show_volume'); + } else { + &exec_cb_script('set_device_name',$device,$volume_name); + my $new_volume_name = &exec_cb_script('get_device_name',$device); + $pagedata->setValue('Data.Warning','SetVolumeNameFailed') unless ($new_volume_name eq $volume_name); + $pagedata->setValue('Data.Action', 'show_volume'); + } + ############ initialize volume (form) ############### + } elsif ($action eq 'volume_init_ask') { + if ($device eq '') { + &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); + $pagedata->setValue('Data.Warning', 'InvalidDevice'); + $pagedata->setValue('Data.Action', 'show_status'); + } elsif (&check_mounted($device)) { + $pagedata->setValue('Data.Warning','VolumeMayNotBeMounted'); + $pagedata->setValue('Data.Action', 'show_volume'); + } else { + $pagedata->setValue('Data.CurrentDisk.InitParams.encrypted',defined($query->param('encryption'))? 1 : 0); + $pagedata->setValue('Data.Action', 'form_init_partition'); + } + ############### initialize volume ################### + } elsif ($action eq 'volume_init_do') { + $current_admin_pw = &get_cbox_config("admin_pw"); + # remember the current "encryption" setting - just in case, we want to emit a warning and + # return to the same screen + $pagedata->setValue('Data.CurrentDisk.InitParams.encrypted',defined($query->param('encryption'))? 1 : 0); + if ($device eq '') { + &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); + $pagedata->setValue('Data.Warning', 'InvalidDevice'); + $pagedata->setValue('Data.Action', 'show_status'); + } elsif (&check_mounted($device)) { + $pagedata->setValue('Data.Warning','VolumeMayNotBeMounted'); + $pagedata->setValue('Data.Action', 'show_volume'); + } elsif ($current_admin_pw ne '' + && $current_admin_pw ne $query->param('current_admin_password')) { + $pagedata->setValue('Data.Warning', 'WrongAdminPassword'); + $pagedata->setValue('Data.Action', 'form_init_partition'); + } elsif ($query->param('crypto_password') ne $query->param('crypto_password2')) { + # different crypto-passwords + $pagedata->setValue('Data.Warning', 'DifferentCryptoPasswords'); + $pagedata->setValue('Data.Action', 'form_init_partition'); + } elsif ($query->param('crypto_password') eq '') { + # empty password + $pagedata->setValue('Data.Warning', 'EmptyCryptoPassword'); + $pagedata->setValue('Data.Action', 'form_init_partition'); + } elsif ($query->param('confirm') ne $pagedata->getValue('Lang.Text.ConfirmInit','')) { + # wrong confirm string + $pagedata->setValue('Data.Warning', 'InitNotConfirmed'); + $pagedata->setValue('Data.Action', 'form_init_partition'); + } else { + if (&volume_init($device,$query->param('crypto_password'))) { + #$pagedata->setValue('Data.Success', 'InitRunning'); + $pagedata->setValue('Data.Action', 'show_volume'); + } else { + $pagedata->setValue('Data.Error', 'InitFailed'); + $pagedata->setValue('Data.Action', 'show_volume'); + } + } + ################## volume info ###################### + } elsif ($action eq 'show_volume') { + if ($device eq '') { + &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); + $pagedata->setValue('Data.Warning', 'InvalidDevice'); + $pagedata->setValue('Data.Action', 'show_status'); + } else { + $pagedata->setValue('Data.Action', 'show_volume'); + } #################### status ######################### } elsif ($action eq 'show_status') { if ( ! &check_config()) { @@ -674,7 +796,7 @@ if ( ! &check_ssl()) { # if we find an existing config partition, then check the adminpw } elsif ($action eq 'do_purge') { if ( &check_config()) { - $current_admin_pw = &get_admin_pw; + $current_admin_pw = &get_cbox_config("admin_pw"); if ($current_admin_pw ne '' && $current_admin_pw ne $query->param('current_admin_password')) { $pagedata->setValue('Data.Warning', 'WrongAdminPassword'); $pagedata->setValue('Data.Action', 'form_config'); @@ -710,20 +832,17 @@ if ( ! &check_ssl()) { # check state of the cryptobox $pagedata->setValue('Data.Status.Config', &check_config() ? 1 : 0); $pagedata->setValue('Data.Status.InitRunning', &check_init_running() ? 1 : 0); -$pagedata->setValue('Data.Status.Mounted', &check_mounted() ? 1 : 0) if (&check_config()); -my $output = &get_current_ip(); -$pagedata->setValue('Data.Status.IP', "$output"); -$output = &get_admin_pw(); +my $output = &get_cbox_config("admin_pw"); $pagedata->setValue('Data.Config.AdminPasswordIsSet', 1) if ($output ne ''); $output = join ("
", &exec_cb_script("diskinfo")); $pagedata->setValue('Data.PartitionInfo',"$output"); # preset config settings for clearsilver -$pagedata->setValue('Data.Config.IP', &exec_cb_script("get_config","ip")); -$pagedata->setValue('Data.Config.TimeOut', &exec_cb_script("get_config","timeout")); -$pagedata->setValue('Data.Config.Language', &exec_cb_script("get_config","language")); +$pagedata->setValue('Data.Config.IP', &get_cbox_config("ip")); +$pagedata->setValue('Data.Config.TimeOut', &get_cbox_config("timeout")); +$pagedata->setValue('Data.Config.Language', &get_cbox_config("language")); # read log and add html linebreaks $output = ''; @@ -742,6 +861,46 @@ my $querystring = $ENV{'QUERY_STRING'}; $querystring =~ s/weblang=\w\w&?//; $pagedata->setValue('Data.QueryString', "$querystring") if ($querystring ne ''); +$pagedata->setValue('Data.Version', CRYPTOBOX_VERSION); + +my ($one_disk, $one_name, $isActive); +my $avail_counter = 0; my $active_counter = 0; my $passive_counter = 0; +for $one_disk (&get_available_disks()) { + $one_name = &get_disk_name($one_disk); + $pagedata->setValue("Data.Disks.available.${avail_counter}.device",$one_disk); + $pagedata->setValue("Data.Disks.available.${avail_counter}.name",$one_name); + $isActive = &check_mounted($one_disk); + if ($isActive) { + $pagedata->setValue("Data.Disks.available.${avail_counter}.isActive",1); + $pagedata->setValue("Data.Disks.active.${active_counter}.device",$one_disk); + $pagedata->setValue("Data.Disks.active.${active_counter}.name",$one_name); + $active_counter++; + } else { + $pagedata->setValue("Data.Disks.available.${avail_counter}.isActive",0); + $pagedata->setValue("Data.Disks.passive.${passive_counter}.device",$one_disk); + $pagedata->setValue("Data.Disks.passive.${passive_counter}.name",$one_name); + $passive_counter++; + } + if ($device eq $one_disk) { + $pagedata->setValue('Data.CurrentDisk.device', $one_disk); + $pagedata->setValue('Data.CurrentDisk.name', $one_name); + $pagedata->setValue('Data.CurrentDisk.active', $isActive? 1 : 0); + # retrieve capacity information if the device is mounted + if (&check_mounted($device)) { + my $cap_info = &exec_cb_script("get_capacity_info",$device); + # filter the relevant values (a simple split is not working, as the device name may + # contain spaces + $cap_info =~ m#^.*\s+([0-9\.,]+\w)\s+([0-9\.,]+\w)\s+([0-9\.,]+\w)\s+([0-9\.,]+\%)\s+#; + my ($cap_size, $cap_used, $cap_free, $cap_percent) = ($1, $2, $3, $4); + $pagedata->setValue('Data.CurrentDisk.capacity.used', $cap_used); + $pagedata->setValue('Data.CurrentDisk.capacity.free', $cap_free); + $pagedata->setValue('Data.CurrentDisk.capacity.size', $cap_size); + $pagedata->setValue('Data.CurrentDisk.capacity.percent', $cap_percent); + } + } + $avail_counter++; +} + &render(); close STDOUT; diff --git a/bin/init-script.sh b/bin/init-script.sh index c9112b2..2dd46db 100755 --- a/bin/init-script.sh +++ b/bin/init-script.sh @@ -40,10 +40,7 @@ case "$1" in ;; stop ) # unmount all active containers - ls ~$CRYPTOBOX_USER/mnt/ | while read mnt_dir - do grep -q " ~$CRYPTOBOX_USER/mnt/$mnt_dir " /proc/mounts || continue - "$LIB_DIR/cbox-manage.sh" crypto-down "$mnt_dir" - done + "$LIB_DIR/cbox-manage.sh" umount_all ;; restart ) "$0" stop diff --git a/bin/ro-system.sh b/bin/ro-system.sh new file mode 100644 index 0000000..2d6e9d7 --- /dev/null +++ b/bin/ro-system.sh @@ -0,0 +1,191 @@ +function create_config() +# Parameter: device +{ + local device=$1 + unload_config + # create the new configuration filesystem if it is not static + if [ "$USE_SEPERATE_CONFIG_PARTITION" != "1" ] + then log_msg "Using static configuration ..." + else log_msg "Creating config filesystem ..." + "$ROOT_PERM_SCRIPT" create_config "$device" + log_msg "Mounting config partition ..." + "$ROOT_PERM_SCRIPT" mount_config "$device" + "$ROOT_PERM_SCRIPT" remount_config rw + fi + log_msg "Copying configuration defaults ..." + cp -a "$CONFIG_DEFAULTS_DIR/." "$CONFIG_DIR" + + log_msg "Copying temporary certificate file to config filesystem ..." + log_msg "Setting inital values ..." + # beware: config_set_value remounts the config partition read-only + config_set_value "ip" "$(get_current_ip)" + # create database of readable names + config_set_value "names.db" "" + # create a marker to recognize a cryptobox partition + # this should be the last step, to prevent a half-initialized state + config_set_value "$CONFIG_MARKER" "$(date -I)" +} + + +function find_harddisk() +# look for the harddisk to be partitioned +{ + local device=$(get_available_disks | head -1) + if [ -z "$device" ] ; then + log_msg "no valid harddisk for initialisation found!" + cat /proc/partitions >>"$LOG_FILE" + # do not return with an error, to avoid a failing of the script ('break on error') + # the caller of this function should handle an empty return string + fi + echo -n "$device" +} + + +function load_config() +{ + unload_config + local status=0 + # look for a configuration partition + [ "$USE_SEPERATE_CONFIG_PARTITION" = "1" ] && \ + list_partitions_of_type config | while read part && [ "$status" = 0 ] + do log_msg "configuraton found on $part" + # error check? + "$ROOT_PERM_SCRIPT" mount_config "/dev/$part" + status=1 + done + if is_config_active + then return 0 + else log_msg "failed to locate config partition" + return 1 + fi +} + + +function unload_config() +{ + is_config_active || return + # only try to unmount, if it is not static (the config of a live-cd is always dynamic) + if [ "$USE_SEPERATE_CONFIG_PARTITION" = "1" ] + then "$ROOT_PERM_SCRIPT" umount_config + else return 0 + fi +} + + +# rename to "prepare_cryptobox" +function init_cryptobox() +# this is only the first part of initialisation that takes no time - good for a smooth web interface +{ + local device=$(find_harddisk) + [ -z "$device" ] && log_msg 'No valid harddisk found!' && return 1 + turn_off_all_crypto + unload_config || true + log_msg "Partitioning the device ($device) ..." + "$ROOT_PERM_SCRIPT" partition_disk "$device" "0,1,L \n,,L\n" + log_msg "Initializing config partition on ${device}1 ..." + # TODO: this should not be hard-coded + create_config "${device}1" +} + + +case "$ACTION" of + network-up ) + if [ "$SKIP_NETWORK_CONFIG" != 1 ] + then conf_ip=$(config_get_value "ip") + log_msg "Configuring $NET_IFACE for $conf_ip ..." + echo "Configuring network interface for $NET_IFACE: $conf_ip" + "$IFCONFIG" "$NET_IFACE" "$conf_ip" + fi + if [ "$EXEC_FIREWALL_RULES" = 1 ] + then log_msg "Starting the firewall ..." + "$FIREWALL_SCRIPT" start + fi + if [ "$USE_STUNNEL" = 1 ] + then # start stunnel + if [ -f "$CERT_FILE" ] + then USE_CERT=$CERT_FILE + else USE_CERT=$CERT_TEMP + $MAKE_CERT_SCRIPT "$CERT_TEMP" >>"$LOG_FILE" 2>&1 + # TODO: this could be dangerous - right? + # this is necessary, to allow www-data to copy the certificate + chown "$WEB_USER" "$CERT_TEMP" + fi + log_msg "Starting stunnel ..." + stunnel -p "$USE_CERT" -r localhost:80 -d 443 \ + || echo "$USE_CERT not found - not starting stunnel" + fi + ;; + network-down ) + if [ "$EXEC_FIREWALL_RULES" = 1 ] + then log_msg "Stopping the firewall ..." + "$FIREWALL_SCRIPT" stop + fi + if [ "$USE_STUNNEL" = 1 ] + then log_msg "Stopping stunnel ..." + # TODO: what about a pid? + killall stunnel 2>/dev/null || true + fi + if [ "$SKIP_NETWORK_CONFIG" != 1 ] + then log_msg "Shutting the network interface down ..." + "$IFCONFIG" "$NET_IFACE" down + fi + ;; + services-up ) + # the mount point has to be writeable + # this action is called as root - so we are allowed to umount + # TODO: do this only for ro-filesystem + # TODO: this way of mounting is evil + if mountpoint -q "$MNT_PARENT" + then true + else mount -t tmpfs tmpfs "$MNT_PARENT" + fi + true + ;; + services-down ) + # this action is called as root - so we are allowed to umount + mountpoint -q "$MNT_PARENT" && umount "$MNT_PARENT" + # TODO: we should not depend on samba and thttpd + # /etc/init.d/samba stop || true + # /etc/init.d/thttpd stop || true + true + ;; + is_harddisk_available ) + [ -z "$(find_harddisk)" ] && exit 1 + exit 0 + ;; + update_ip_address ) + # reconfigure the network interface to a new IP address + # wait for 5 seconds to finish present http requests + if [ "$SKIP_NETWORK_CONFIG" != 1 ] + then echo -n "sleep 5; \"$ROOT_PERM_SCRIPT\" update_network" | at now + fi + ;; + poweroff ) + log_msg "Turning off the CryptoBox ..." + turn_off_all_crypto + echo "poweroff" | at now + ;; + reboot ) + log_msg "Rebooting the CryptoBox ..." + turn_off_all_crypto + echo "reboot" | at now + ;; + * ) + echo "Syntax: $(basename $0) ACTION" + echo " config-up - scan for configuration partition and mount it" + echo " config-down - unmount configuration partition" + echo " network-up - enable network interface" + echo " network-down - disable network interface" + echo " services-up - run some cryptobox specific daemons" + echo " services-down - stop some cryptobox specific daemons" + echo " update_ip_address - update the network interface after reconfiguration" + echo " is_config_mounted - check, if configuration partition is mounted" + echo " box-init - initialize cryptobox (ALL data is LOST)" + echo " box-init-fg - the first part of initialization" + echo " box-init-bg - the last part of initialization (background)" + echo " is_harddisk_available - check, if there is a usable harddisk" + echo " poweroff - shutdown the cryptobox" + echo " reboot - reboot the cryptobox" + echo + ;; + esac diff --git a/conf-examples/cryptobox.conf b/conf-examples/cryptobox.conf index e1f7f48..4ac399a 100644 --- a/conf-examples/cryptobox.conf +++ b/conf-examples/cryptobox.conf @@ -2,14 +2,8 @@ # so there should be no space around the "=" LANGUAGE=en -NET_IFACE=eth0 CRYPTOBOX_USER=cryptobox -SCAN_DEVICES="sda" -#SCAN_DEVICES="hda hdb hdc hdd hde hdf hdg scd sg sda sdb sdc sdd" - -# set this to "1" to create a dedicated configuration partition (very small) -# this should only be necessary, if your root filesystem is read-only (as for a live-cd) -USE_SEPERATE_CONFIG_PARTITION=0 +SCAN_DEVICES="sd loop" # web interface HTML_TEMPLATE_DIR=/usr/share/cryptobox/templates @@ -22,7 +16,6 @@ CONFIG_DEFAULTS_DIR=/usr/share/cryptobox/defaults # some files LOG_FILE=/var/log/cryptobox.log -CONFIG_MARKER=cryptobox.marker # crypto settings # TODO: for now, the usual default cipher does not work on ARM, so we enable it during development @@ -33,3 +26,11 @@ DEV_MAPPER_DIR=/dev/mapper # distribution specific configuration # examples can be found in /usr/local/share/cryptobox/distributions DISTRIBUTION_CONF=/etc/cryptobox/distribution.conf + +# choose a debug level: +# 0 => no debug messages at all +# 1 => critical errors (default) +# 2 => warning messages +# 3 => information +DEBUG_LEVEL=1 + diff --git a/conf-examples/distributions/gnu_linux b/conf-examples/distributions/gnu_linux index e50a928..08bc7b6 100644 --- a/conf-examples/distributions/gnu_linux +++ b/conf-examples/distributions/gnu_linux @@ -6,4 +6,7 @@ CRYPTSETUP=/sbin/cryptsetup IFCONFIG=/sbin/ifconfig PMOUNT=/bin/pmount PUMOUNT=/bin/pumount +DUMPE2FS=/sbin/dumpe2fs +POWEROFF=/sbin/poweroff +REBOOT=/sbin/reboot diff --git a/debian/control b/debian/control index 6ef41aa..9930ada 100644 --- a/debian/control +++ b/debian/control @@ -7,9 +7,9 @@ Standards-Version: 3.6.2 Package: cryptobox Architecture: any -Depends: bash (>=2.0), sed (>=4.0), coreutils, grep (>=2.0), perl, httpd-cgi, hashalot, libconfigfile-perl, cryptsetup (>=20050111), dmsetup, at, pmount +Depends: bash (>=2.0), sed (>=4.0), coreutils, grep (>=2.0), perl, httpd-cgi, hashalot, libconfigfile-perl, cryptsetup (>=20050111), dmsetup, pmount, initscripts Recommends: perl-clearsilver -Suggests: stunnel, cron, iptables, samba +Suggests: cron, samba Description: Web interface for an encrypting fileserver This bundle of scripts and cgis allow you to manage an encrypted harddisk via a web interface. The data is platform independently available diff --git a/debian/prerm b/debian/prerm index 4be1943..ad590f6 100755 --- a/debian/prerm +++ b/debian/prerm @@ -1,5 +1,7 @@ #!/bin/sh +# TODO: we should remove everything, except for "invoke-rc.d" + # read the default setting file, if it exists [ -e /etc/default/cryptobox ] && . /etc/default/cryptobox @@ -8,7 +10,7 @@ CONF_FILE=${CONF_FILE:-/etc/cryptobox/cryptobox.conf} # parse config file if test -e "$CONF_FILE" - then . "$CONF_FILE" + then source "$CONF_FILE" else echo "[$(basename $0)] - configuration file ($CONF_FILE) not found!" >&2 exit 1 fi diff --git a/lang/de.hdf b/lang/de.hdf index fcf9bc4..4bfd1ee 100644 --- a/lang/de.hdf +++ b/lang/de.hdf @@ -12,7 +12,7 @@ Lang { Umount = Deaktivierung der Crypto-Daten Config = Konfiguration der CryptoBox Log = Protokoll der CryptoBox - ShutDown = System + System = System Status = Status der CryptoBox } @@ -44,7 +44,7 @@ Lang { Button { - DoInit = CryptoBox initialisieren + DoInit = Initialisierung SaveConfig = Speichere Konfiguration Update = Aktualisieren Mount = Crypto-Daten aktivieren @@ -52,8 +52,8 @@ Lang { Config = Einstellungen PowerOff = ausschalten ReBoot = neu starten - Protocol = Protokoll - Documentation = Handbuch + Protocol = Protokoll anzeigen + Documentation = Hilfe Status = Status } diff --git a/lang/en.hdf b/lang/en.hdf index 5236178..368de40 100644 --- a/lang/en.hdf +++ b/lang/en.hdf @@ -12,8 +12,9 @@ Lang { Umount = Deactivation of encrypted data Config = CryptoBox configuration Log = CryptoBox logfiles - ShutDown = System + System = System Status = Status + Volume = Properties of } @@ -24,10 +25,10 @@ Lang { EnterCurrentAdminPassword = Enter the current administration password: EnterNewAdminPassword = Enter the new administration password: EnterSameAdminPassword = Repeat the new administration password: - InitWarning = During the process of initialization ALL DATA on the hard drive WILL BE DELETED! + InitWarning = During the process of initialization ALL DATA on the disk WILL BE DELETED! ConfirmInitHint = To confirm that you know what you are doing, please enter exactly the following sequence: ConfirmInit = Yes, delete all data! - PartitionInfo = Current partioning of the hard drive: + PartitionInfo = Current partioning of the disk: IPAddress = Network address (IP) of the CryptoBox: TimeOut = Timeout for deactivation of the encrypted filesystem (in minutes): EmptyLog = The logfiles of the CryptoBox are empty. @@ -40,11 +41,17 @@ Lang { Configuration = Configuration CryptoIsActive = The encrypted data is accessible. CryptoIsDown = The encrypted data is protected from any access. + ChoosePartition = Which container do you want to enable? + ChosenPartition = The chosen container is + ActivePartitions = The following containers are enabled + PassivePartitions = The following containers are disabled + ContainerName = Container's name: + ContainerEncryption = Enable encryption } Button { - DoInit = Initialize CryptoBox + DoInit = Initialization SaveConfig = Save configuration Update = Refresh Mount = Activate encrypted filesystem @@ -52,9 +59,12 @@ Lang { Config = Configuration PowerOff = Shutdown ReBoot = Reboot - Protocol = Logfiles - Documentation = Manual + Protocol = Show logfiles + Documentation = Help Status = Status + System = System + ContainerNameSet = Change name + InitContainer = Initialize container } @@ -91,7 +101,7 @@ Lang { UmountFailed { Title = Deactivation failed - Text = The encrypted filesystem could not be activated. Probably some files are still in use. Close all unclean programs (for example that widely used word processor). In case of need just shut down the CryptoBox! + Text = The encrypted filesystem could not be deactivated. Probably some files are still in use. Close all unclean programs (for example that widely used word processor). In case of emergency just shut down the CryptoBox! } NotInitialized { @@ -153,6 +163,36 @@ Lang { Title = Change of network address Text = The network address has been changed. In a few seconds you will get redirected to the new address. } + + NoDiskAvailableForMount { + Title = No partition available + Text = There is no unused container available. Maybe all containers are already mounted? + } + + NoDiskAvailableForUmount { + Title = No partition available + Text = There is no active container available for turning off. Maybe there is no active container? + } + + InvalidDevice { + Title = Invalid device + Text = The device you have chosen is invalid! + } + + InvalidVolumeName { + Title = Changing of container's name failed + Text = The supplied new name of the container was invalid. Please try again! + } + + SetVolumeNameFailed { + Title = Changing of container's name failed + Text = Could not change the name of the container. Take a look at the log files for details. + } + + VolumeMayNotBeMounted { + Title = The container is mounted + Text = This action is not available while the container is active. Please turn it off first. + } } @@ -207,8 +247,8 @@ Lang { } NoHardDisk { - Title = No hard drive - Text = No hard drive suitable for saving the encrypted filesystem found. Please ensure the BIOS detected the hard drive during power-on of the comuter. + Title = No hard disk + Text = No disk suitable for an encrypted filesystem found. Please ensure the BIOS detected the disk during power-on of the computer. } } diff --git a/scripts/check_languages.sh b/scripts/check_languages.sh new file mode 100755 index 0000000..502dbe9 --- /dev/null +++ b/scripts/check_languages.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# compare the defined fields of a language file with the english translation +# +# nice for finding unavailable definitions +# +# Parameter: LANGUAGE +# (e.g. "de") +# + +set -u + +LANG_DIR=$(dirname $0)/../lang +DEFAULT_LANG=en +TMP_FILE1=/tmp/$(basename $0)-$$-1 +TMP_FILE2=/tmp/$(basename $0)-$$-2 + +[ $# -ne 1 ] && echo -e "Syntax: $(basename $0) LANGUAGE\n" >&2 && exit 1 + +grep "=" "$LANG_DIR/${DEFAULT_LANG}.hdf" | grep -v "^[[:space:]]*#" | cut -f 1 -d "=" >"$TMP_FILE1" +grep "=" "$LANG_DIR/${1}.hdf" | grep -v "^[[:space:]]*#" | cut -f 1 -d "=" >"$TMP_FILE2" + +diff -wu "$TMP_FILE1" "$TMP_FILE2" + +rm "$TMP_FILE1" "$TMP_FILE2" + diff --git a/templates/empty.cs b/templates/empty.cs index 8db0381..a8716a3 100644 --- a/templates/empty.cs +++ b/templates/empty.cs @@ -1,3 +1,3 @@ - + - + diff --git a/templates/error.cs b/templates/error.cs index ac56d9a..18ac8f1 100644 --- a/templates/error.cs +++ b/templates/error.cs @@ -1,3 +1,3 @@ - + diff --git a/templates/footer.cs b/templates/footer.cs index afb8a6b..5039dae 100644 --- a/templates/footer.cs +++ b/templates/footer.cs @@ -1,18 +1,18 @@ - + -

+

@@ -29,7 +29,8 @@ Data.Status.IP= Data.Status.Mounted= CBOX-STATUS-end --> - + + diff --git a/templates/form_config.cs b/templates/form_config.cs index fcc7ee4..75e94d2 100644 --- a/templates/form_config.cs +++ b/templates/form_config.cs @@ -1,31 +1,34 @@ - +

-


-

+


+

-


-
+

+ --> -


-
+

-


-

- +
+ diff --git a/templates/form_init.cs b/templates/form_init.cs index a871cc3..0d0c415 100644 --- a/templates/form_init.cs +++ b/templates/form_init.cs @@ -1,4 +1,4 @@ - +

@@ -6,19 +6,7 @@

- -


-

- -


-

-


-

-


-

-


-

-


@@ -28,7 +16,11 @@
+ diff --git a/templates/form_init_partition.cs b/templates/form_init_partition.cs new file mode 100644 index 0000000..e96a188 --- /dev/null +++ b/templates/form_init_partition.cs @@ -0,0 +1,33 @@ + + +

+ + + +

+ +


+

+ +


+

+


+ +

+ + +


+

+ +

+ +

+ + diff --git a/templates/form_mount.cs b/templates/form_mount.cs index a55dc00..3624ebf 100644 --- a/templates/form_mount.cs +++ b/templates/form_mount.cs @@ -1,15 +1,39 @@ - + -

+ -
- + -

-

+ - - +

+ +
+ + + + 1 ?> +

+

+ +

:

+ + +

+

+ + + + + +
+ + - -
diff --git a/templates/form_shutdown.cs b/templates/form_shutdown.cs deleted file mode 100644 index 3a27662..0000000 --- a/templates/form_shutdown.cs +++ /dev/null @@ -1,19 +0,0 @@ - - -

- -
- - - - - - -   - - - - - - -
diff --git a/templates/form_system.cs b/templates/form_system.cs new file mode 100644 index 0000000..d712006 --- /dev/null +++ b/templates/form_system.cs @@ -0,0 +1,29 @@ + + +

+ +

+ + diff --git a/templates/form_umount.cs b/templates/form_umount.cs index f11b963..ed16099 100644 --- a/templates/form_umount.cs +++ b/templates/form_umount.cs @@ -1,12 +1,37 @@ - + -

+ -
- + - - + + +

+ +
+ + + 1 ?> +

+

+ + +

:

+ + + + + + +
+ + - -
diff --git a/templates/header.cs b/templates/header.cs index e87dcf8..8286ff6 100644 --- a/templates/header.cs +++ b/templates/header.cs @@ -1,4 +1,4 @@ - + @@ -12,20 +12,26 @@ + elif:Data.Redirect.Action ?> + 0 ?> +
+ +
+ +
- &">
- + 0 ?>
@@ -37,7 +43,7 @@
+ diff --git a/templates/macros.cs b/templates/macros.cs index c58624f..7699f26 100644 --- a/templates/macros.cs +++ b/templates/macros.cs @@ -1,42 +1,45 @@ - +
-

-

- +def:warning(warnname) + ?>
+

+

+ +

unknown warning message

+

could not find warning message: ''

+
-

-

- +def:error(errname) + ?>
+

+

+ +

unknown error message

+

could not find error message: ''

+
-

-

- +def:success(succname) + ?>
+

+

+ +

unknown success message

+

could not find success message: ''

+
- + --> 0 + ?>?&=
" method="post" enctype="application/x-www-from-urlencoded" accept-charset="utf-8"> - - - - - - diff --git a/templates/main.cs b/templates/main.cs index d93cc61..33a2335 100644 --- a/templates/main.cs +++ b/templates/main.cs @@ -1,9 +1,9 @@ - + - + diff --git a/templates/nav.cs b/templates/nav.cs index 5528d7a..5a7cfbc 100644 --- a/templates/nav.cs +++ b/templates/nav.cs @@ -1,22 +1,26 @@ - + - + - - - - - - + + + + - - - - + + - - - - + + diff --git a/templates/show_doc.cs b/templates/show_doc.cs index 20146db..4f8af80 100644 --- a/templates/show_doc.cs +++ b/templates/show_doc.cs @@ -1,4 +1,4 @@ - +
diff --git a/templates/show_log.cs b/templates/show_log.cs index 3544b63..16b0092 100644 --- a/templates/show_log.cs +++ b/templates/show_log.cs @@ -1,4 +1,4 @@ - +
diff --git a/templates/show_status.cs b/templates/show_status.cs index 00e2ab4..cf8da61 100644 --- a/templates/show_status.cs +++ b/templates/show_status.cs @@ -1,5 +1,28 @@ - +

-

-

+ + + + +

Sorry - you should have never seen this ...

+ + + + + + + + + 0 ?> +

: +

    +
+ 0 ?> +

: +

    +
+ + diff --git a/templates/show_volume.cs b/templates/show_volume.cs new file mode 100644 index 0000000..5dbd706 --- /dev/null +++ b/templates/show_volume.cs @@ -0,0 +1,64 @@ + + +

+ + + +

Mount container

+ +

+ + + +

+ + +

Unmount container

+ +

+ +

+ + + + + +

Change the name of the container

+ +

+ + + +

+ + + + + + +

Reinitialize container

+ +

+ + + +

+ + + +

Some more stuff

+

For example: changing password? / backup? / access control?

+ +

Details

+

    +
  • Name of container:
  • +
  • Name of device:
  • +
  • Current status: activepassive
  • + +
  • Size of container:
  • +
  • Available space of container:
  • +
  • Used space of container: /
  • + +

+ diff --git a/templates/show_volumes.cs b/templates/show_volumes.cs new file mode 100644 index 0000000..7a5d8a8 --- /dev/null +++ b/templates/show_volumes.cs @@ -0,0 +1,15 @@ + + + + + + +
+

+
+
+ + + diff --git a/www-data/cryptobox.css b/www-data/cryptobox.css index e12c610..3fc24c1 100644 --- a/www-data/cryptobox.css +++ b/www-data/cryptobox.css @@ -273,6 +273,7 @@ button { font-size: 0.8em; font-weight: bold; cursor: pointer; + margin: 2px 10px 2px 10px; } button:hover { @@ -292,6 +293,38 @@ button:hover { text-align: center; } +/* -------------=-=-=- volume selection -=-=-=-------------- */ + +#volumes { + position: absolute; + float: left; + right: 5px; + left: 5px; + width: 140px; + } + +#volumes div { + height: 80px; + padding: 5px; + margin: 5px; + background-repeat: no-repeat; + background-position: center; + } + +#volumes div.active { + background-image: url(disc_red.png); + } + +#volumes div.passive { + background-image: url(disc_green.png); + } + +#volumes div.current { + border-style: dashed; + border-width: 2px; + border-color: gray; + } + /* ------------=-=-=- language selection -=-=-=------------- */ #lang { diff --git a/www-data/disc_green.png b/www-data/disc_green.png new file mode 100644 index 0000000..887bd54 Binary files /dev/null and b/www-data/disc_green.png differ diff --git a/www-data/disc_red.png b/www-data/disc_red.png new file mode 100644 index 0000000..7ee121a Binary files /dev/null and b/www-data/disc_red.png differ