#!/bin/sh # # Copyright (c) 02005 sense.lab # # 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 test -e /etc/default/cryptobox && . /etc/default/cryptobox test ! -e "$CONF_FILE" && echo "Could not find the configuration file: $CONF_FILE" >&2 && exit 1 # parse config file . "$CONF_FILE" test ! -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 test -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 ... test -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 test $# -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" } # list which allowed disks are at the moment connected with the cbox function get_available_disks() { for scan in $SCAN_DEVICES do for avail in $ALL_PARTITIONS do echo "$avail" | grep -q "^$scan[^/]*" && echo "/dev/$avail" done done return 0 } # Parameter: DEVICE function mount_crypto() { local device=$1 test -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 test $# -gt 0 && ACTION=$1 && shift case "$ACTION" in crypto-up ) test $# -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 ) test $# -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 ;; list_container ) test $# -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 test $# -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 test $# -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 ) test $# -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 ) test $# -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 ) test $# -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 ) test $# -ne 2 && error_msg 7 "'set_config' requires two parameters" config_set_value "$1" "$2" ;; get_config ) test $# -ne 1 && error_msg 6 "'get_config' requires exactly one parameter" config_get_value "$1" ;; get_capacity_info ) test $# -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