#!/bin/sh # $Id$ # # this script does EVERYTHING # all other scripts are only frontends :) # # called by: # - some rc-scripts # - the web frontend cgi # set -eu # parse config file . /etc/cryptobox/cryptobox.conf ## configuration CONFIG_MARKER="$CONFIG_DIR/cryptobox.marker" CERT_TEMP=/tmp/stunnel.pem ##### function log_msg() { # the log file is not writable during boot - try before writing ... [ -w "$LOG_FILE" ] || return 0 echo >>"$LOG_FILE" echo "##### `date` #####" >>"$LOG_FILE" echo "$1" >>"$LOG_FILE" } function error_msg() # parameters: ExitCode ErrorMessage { echo "[`date`] - $2" | tee -a "$LOG_FILE" >&2 # print the execution stack - not usable with busybox #caller | sed 's/^/\t/' >&2 exit "$1" } function initial_checks() # Parameter: device { local device="$1" [ ! -b "$device" ] && log_msg "blockdevice $device does not exist" && return 1 [ ! -x "$WIPE" ] && log_msg "$WIPE not found" && return 1 [ ! -x "$SFDISK" ] && log_msg "$SFDISK not found" && return 1 for a in $ALGO $HASH do grep -q "^name *: $a$" /proc/crypto || modprobe "$a" grep -q "^name *: $a$" /proc/crypto || { log_msg "$a is not supported by kernel" && return 1; } done log_msg "inital checks successful" return 0 } function create_partitions() # Parameter: device { local device="$1" # first partition size is 1 sector, second goes til end # sfdisk -n doesn't actually write (for testing purpose) echo -e "0,1,L \n,,L\n" | $SFDISK "$device" } function config_set_value() # parameters: SettingName SettingValue { mount -o rw,remount "$CONFIG_DIR" echo -n "$2" > "$CONFIG_DIR/$1" mount -o ro,remount "$CONFIG_DIR" } function config_get_value() # parameters: SettingName { # use mounted config, if it exists - otherwise use defaults local conf_dir if is_config_mounted then conf_dir=$CONFIG_DIR else conf_dir=$CONFIG_DEFAULTS_DIR fi [ -z "$1" ] && error_msg 1 "empty setting name" [ ! -e "$conf_dir/$1" ] && error_msg 2 "unknown configuration value ($1)" # remove trailing line break echo -n $(cat "$conf_dir/$1") } function create_config() # Parameter: device { local device="${1}1" log_msg "Creating config filesystem ..." # filter output through 'tr' to replace tabs $MKFS_CONFIG "$device" | tr '\010' ' ' # mount the config partition rw log_msg "Mounting config partition ..." mount "$device" "$CONFIG_DIR" # create a marker to recognize a cryptobox partition date -I >"$CONFIG_MARKER" log_msg "Copying configuration defaults ..." cp -a "$CONFIG_DEFAULTS_DIR/." "$CONFIG_DIR" log_msg "Copying temporary cerificate file to config filesystem ..." # beware: the temp file should always be there - even after reboot - see "mount_config" cp -p "$CERT_TEMP" "$CERT_FILE" log_msg "Setting inital values ..." # beware: config_set_value remounts the config partition read-only config_set_value "device" "$1" config_set_value "ip" "$(get_current_ip)" # reinitialise configuration log_msg "Unmounting config partition ..." umount "$CONFIG_DIR" log_msg "Reload configuration ..." mount_config } 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) } function create_crypto() # Parameter: device { local device="$1" # passphrase may be passed via command line $CRYPTSETUP -h "$HASH" -c "$ALGO" create "`basename $CRYPTMAPPER_DEV`" "${device}2" } function mkfs_crypto() # split from create_crypto to allow background execution via web interface { local device=$(find_harddisk) # flood the crypto partition with noise # writing to the real partition is faster # TODO: this takes _much_ too long - maybe add a "secure wipe" switch to the interface? #dd if=/dev/urandom of="${device}2" bs=512 # filter output through 'tr' to replace tabs $MKFS_DATA "$CRYPTMAPPER_DEV" | tr '\0101' ' ' } function config_mount_test() # Parameter: device { local device="${1}" local STATUS=0 mount "${device}1" "$CONFIG_DIR" &>/dev/null || true is_config_mounted && STATUS=1 umount "$CONFIG_DIR" &>/dev/null || true # return code is the result of this expression [ 1 -eq "$STATUS" ] && return 0 return 1 } function is_config_mounted() { mount | grep -q " ${CONFIG_DIR} " && [ -f "$CONFIG_MARKER" ] } function is_crypto_mounted() { mount | grep -q " ${CRYPTO_DIR} " } function is_init_running() { check_at_command_queue " box-init-bg" } # 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 dev=$( if is_config_mounted then config_get_value "device" else for a in $SCAN_DEVICES do grep -q " `basename $a`$" /proc/partitions && echo "$a" && break done fi ) [ -z "$dev" ] && echo "no valid partition for initialisation found!" >>"$LOG_FILE" echo -n "$dev" } function mount_config() { is_config_mounted && error_msg 3 "configuration directory ($CONFIG_DIR) is already mounted!" local device=$( for a in $SCAN_DEVICES do log_msg "Trying to load configuration from $a ..." config_mount_test "$a" && echo "$a" && break done ) if [ -n "$device" ] && mount "${device}1" "$CONFIG_DIR" then log_msg "configuraton found on $device" config_set_value "device" "$device" # copy certificate to /tmp in case of re-initialization # /tmp should be writable, so tmpfs has to be mounted before (/etc/rcS.d) cp "$CERT_FILE" "$CERT_TEMP" return 0 else log_msg "failed to locate harddisk" return 1 fi } function mount_crypto() { is_crypto_mounted && echo "Das Crypto-Dateisystem ist bereits aktiv!" && return local device=`find_harddisk` [ -z "$device" ] && error_msg 4 'no valid harddisk found!' # passphrase is read from stdin log_msg "Mounting crypto partition ..." $CRYPTSETUP -h "$HASH" -c "$ALGO" create "`basename $CRYPTMAPPER_DEV`" "${device}2" if mount "$CRYPTMAPPER_DEV" "$CRYPTO_DIR" then log_msg "Mount succeded - now starting samba ..." /etc/init.d/samba start else log_msg "Mount failed - removing dev-mapper ..." dmsetup remove $(basename $CRYPTMAPPER_DEV) return 1 fi } function umount_crypto() { # do not break on error set +e if ps -e | grep -q " [sn]mbd$" then log_msg "Stopping samba ..." /etc/init.d/samba stop ps -e | grep -q " smbd$" && killall smbd ps -e | grep -q " nmbd$" && killall nmbd ps -e | grep -q " smbd$" && killall -9 smbd ps -e | grep -q " nmbd$" && killall -9 nmbd fi if mount | grep -q " $CRYPTO_DIR " then log_msg "Unmounting crypto partition ..." umount "$CRYPTO_DIR" fi if [ -e "$CRYPTMAPPER_DEV" ] then log_msg "Removing dev-mapper ..." $CRYPTSETUP remove $(basename $CRYPTMAPPER_DEV) fi set -e } function init_cryptobox_part1() # 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 ( log_msg "Initializing crypto partition on $device ..." umount_crypto || true mount | grep -q " $CONFIG_DIR " && umount "$CONFIG_DIR" || true initial_checks "$device" || return 1 create_partitions "$device" create_config "$device" ) >>"$LOG_FILE" 2>&1 # the output of create_crypto may NOT be redirected - this would prevent cryptsetup from # reading the passphrase from stdin log_msg "Creating the crypto partition ..." create_crypto "$device" } function init_cryptobox_part2() # some things to be done in the background # these are the final steps of initialisation # the uid must be changed initially, therfore it needs to be mounted { mkfs_crypto mount "$CRYPTMAPPER_DEV" "$CRYPTO_DIR" chown $SAMBA_USER "$CRYPTO_DIR" umount_crypto } function init_cryptobox_complete() { init_cryptobox_part1 init_cryptobox_part2 } ### main ### # set PATH because thttpd removes /sbin and /usr/sbin for cgis export PATH=/usr/sbin:/usr/bin:/sbin:/bin ACTION=help [ $# -gt 0 ] && ACTION="$1" case "$ACTION" in config-up ) if mount_config then echo "Cryptobox configuration successfully loaded" else error_msg 3 "Could not find a configuration partition!" fi ;; config-down ) umount "$CONFIG_DIR" || error_msg 4 "Could not unmount configuration partition" ;; network-up ) kudzu -s -q --class network conf_ip=$(config_get_value "ip") ifconfig $NET_IFACE "$conf_ip" log_msg "Configured $NET_IFACE for $conf_ip ..." echo "Configured network interface for $NET_IFACE: $conf_ip" log_msg "Starting the firewall ..." "$FIREWALL_SCRIPT" start # 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 fi log_msg "Starting stunnel ..." stunnel -p "$USE_CERT" -r localhost:80 -d 443 \ || echo "$USE_CERT not found - not starting stunnel" # this ping allows other hosts to get the IP of # the box, in case of misconfiguration ping -b -c 1 $(ifconfig $NET_IFACE | grep Bcast | cut -d ":" -f 3 | cut -d " " -f 1) &>/dev/null ;; network-down ) log_msg "Stopping the firewall ..." "$FIREWALL_SCRIPT" stop log_msg "Stopping stunnel ..." killall stunnel log_msg "Shutting the network interface down ..." ifconfig $NET_IFACE down ;; services-up ) # is something special necessary? ;; services-down ) /etc/init.d/samba stop /etc/init.d/thttpd stop ;; crypto-up ) mount_crypto ;; crypto-down ) umount_crypto ;; box-init ) # do complete initialization "$0" box-init-fg # the background part will recall itself as an at-command "$0" box-init-bg ;; box-init-fg ) # only partitioning and configuration # this is nice for the web interface, as it is fast # output redirection does not work, as it prevents cryptsetup from asking # for a password init_cryptobox_part1 ;; box-init-bg ) # do it in the background to provide a smoother web interface # messages and errors get written to $LOG_FILE # make sure, that this is always called via 'at': if check_at_command_queue " box-init-bg" then init_cryptobox_part2 >"$LOG_FILE" 2>&1 else echo -n "'$0' box-init-bg" | at now fi ;; is_crypto_mounted ) is_crypto_mounted ;; is_config_mounted ) is_config_mounted ;; 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 echo -n "sleep 5; ifconfig $NET_IFACE `config_get_value ip`" | at now ;; get_current_ip ) get_current_ip ;; set_config ) [ $# -ne 3 ] && error_msg 7 "'set_config' requires two parameters" config_set_value "$2" "$3" ;; get_config ) [ $# -ne 2 ] && error_msg 6 "'get_config' requires exactly one parameter" config_get_value "$2" ;; diskinfo ) $SFDISK -L -q -l `find_harddisk` ;; poweroff ) is_crypto_mounted && umount_crypto log_msg "Turning off the CryptoBox ..." echo "poweroff" | at now ;; reboot ) is_crypto_mounted && umount_crypto log_msg "Rebooting the CryptoBox ..." echo "reboot" | at now ;; clean ) # only for development log_msg "Cleaning the CryptoBox ..." device=$(find_harddisk) $0 crypto-down $0 config-down # TODO: test this! echo -e ";\n;\n;\n;\n" | $SFDISK "$device" ;; * ) 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 " crypto-up - mount crypto partition and start samba" echo " crypto-down - unmount crypto partition and stop samba" 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_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 " diskinfo - show the partition table of the harddisk" echo " poweroff - shutdown the cryptobox" echo " clean - remove all partitions [only for development]" echo " reboot - reboot the cryptobox" echo ;; esac