#!/usr/bin/bash

if [ $(/usr/bin/id -u) != 0 ]; then echo "only root can do that"; exit 2; fi

#
# Copyright 2015-2017 Senderek Web Security, Ireland. All rights reserved.
#                <https://senderek.ie>
#
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

#
# Author:       Ralf Senderek <innovation@senderek.ie>
#
# license:      GNU General Public License version 3 or higher
# description:  Activates the cryptobone daemon during the boot process 
# processname:  cryptoboned 
# config:       none
# date:         8 November 2016
#

### BEGIN INIT INFO
# Provides:          cryptoboned
# Required-Start:    $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Activates the cryptobone daemon during the boot process
### END INIT INFO

# chkconfig:         2345  90 60

if [ -f /lib/lsb/init-functions ]
then
    . /lib/lsb/init-functions
fi 
KEYS="/usr/lib/cryptobone/keys"
REAL="none"
SOCK="/usr/lib/cryptobone/secrets.sock"

##########################################################################
log_debug() {
     # make sure, non-sensitive information is stored for debugging purposes
     # if  a file /root/.cryptobone.debug exists with permissions 0600 for the
     # root user

     DEB=/root/.cryptobone.debug
     if /bin/ls $DEB 2> /dev/null > /dev/null ; then
          PERM=$(/bin/ls -l $DEB | cut -c2-10)
	  TIME=$(date '+%T %N')
          if [[ $PERM = "rw-------" ]] ; then
	       if [[ x$1 = xclean ]] ; then
                    echo -n > $DEB
	       fi
	       echo "$TIME $1 $2" >> $DEB
	  fi 
     fi
}

##########################################################################
start() {

     if ! df | grep /usr/lib/cryptobone/keys 2> /dev/null > /dev/null
     then
           # we're booting 
           # the package rng-tools should be installed and the rngd enabled by default
	   # starting rngd here will slow down the shutdown / stop job for cryptoboned
	   #/usr/sbin/rngd -b -r /dev/hwrnd 2> /dev/null

	   # load the SELinux policy for the daemon
	   log_debug "clean"
           if selinuxenabled ; then
	        log_debug "start selinux module"
	        semodule -i /usr/lib/cryptobone/selinux/cryptobone.pp
                semodule -e cryptobone
	        log_debug "end selinux module"
	   fi

           SWITCH=/usr/lib/cryptobone/bootswitch
           if [ ! -f $SWITCH ] 
           then
               touch $SWITCH
           fi
           chattr -i $SWITCH
           chmod 600 $SWITCH
	   chmod 700 $KEYS
          
	   if [ ! -L /usr/lib/cryptobone/masterkey ]
	   then
                # initialize database and create a master key and local key
		/usr/lib/cryptobone/createmasterkey
		# now there is $KEYS/random and both file systems (boot.fs and permanent.fs)
	   fi
	   /bin/rm /usr/lib/cryptobone/UPLOADED 2> /dev/null
	   # record evidence that we're really booting
           typeset -i BOOTTIME=$(cat /proc/stat | grep btime | cut -f2 -d" ")
           typeset -i NOW=$(date +%s)
	   typeset -i DIFF=$NOW-$BOOTTIME
	   echo $DIFF > $SWITCH
	   log_debug "booting"
           DIR=$(echo -n $(cat $KEYS/random) $(stat -c "directory %i" $KEYS) | sha256sum | cut -c-64)
	   FILE=$(echo -n $DIR $(stat -c "file %i"  $KEYS/random) | sha256sum | cut -c-64)

           # establish the boot file system
	   L=$(losetup -f)
	   losetup $L $KEYS/boot.fs
           cat $KEYS/$DIR/$FILE | cryptsetup create cryptobone $L
	   if mount /dev/mapper/cryptobone $KEYS
	   then
	        # start the daemon unless there is a non-empty database
		if [ ! -s /usr/lib/cryptobone/database ] ; then
		     /bin/rm /usr/lib/cryptobone/database
		     log_debug "performing database initialisation"
		     systemctl start cryptobone-dbinit
		     /bin/sleep 1
                     typeset -i x=0
		     while [ $x -lt 8 ]
                     do
		          if [ ! -s /usr/lib/cryptobone/database ] ; then
		              log_debug "restarting database initialisation"
                              systemctl stop cryptobone-dbinit
			      /bin/rm /usr/lib/cryptobone/database
                              systemctl start cryptobone-dbinit
                              /bin/sleep 1
                              x=$x+1
                          else
                              x=10
                          fi
                     done
		fi
		rm -f /usr/lib/cryptobone/ssh.sock
		rm -f /usr/lib/cryptobone/secrets.sock
		log_debug "starting daemon ... "
	        /usr/lib/cryptobone/cryptoboned 
		log_debug "daemon started"
	        ssh-agent -s -a /usr/lib/cryptobone/ssh.sock
		export SSH_AUTH_SOCK=/usr/lib/cryptobone/ssh.sock 2>/dev/null > /dev/null


                OVERWRITE=$(echo "get-element EXTERN.OVERWRITE"  | socat - UNIX-connect:$SOCK 2> /dev/null)
                if [ x$OVERWRITE = "xyes" ]
		then
                     rm -f $KEYS/real.key $KEYS/cbb
		fi

		if  [ ! -r $KEYS/real.key ]
		then
                     # check if external keys are stored in the encrypted database
                     REALKEY=$(echo "get-element EXTERN.real.key"  | socat - UNIX-connect:$SOCK 2> /dev/null)
                     if [ x$REALKEY != "x" ]
		     then
                          echo $REALKEY > $KEYS/real.key
			  chmod 600 $KEYS/real.key
			  log_debug "overwriting master key"
	                  REALKEY="000000000000000000000000000000000000000000"
	                  unset REALKEY
		     fi
                fi

		if  [ ! -r $KEYS/cbb ]
                then
                     SSHKEY=$(echo "get-element EXTERN.cbb"  | socat - UNIX-connect:$SOCK 2> /dev/null)
                     if [ x$SSHKEY != "x" ]
		     then
                          echo "$SSHKEY" | base64 -d  > $KEYS/cbb
			  chmod 600 $KEYS/cbb
	                  SSHKEY=$(/bin/dd if=/dev/zero bs=1K count=4 2>/dev/null)
	                  unset SSHKEY
		     fi
		fi
		
                echo -n "replace EXTERN.OVERWRITE no"  | socat - UNIX-connect:$SOCK 2> /dev/null
		
		if [ -f $KEYS/cbb ]
		then
		     ssh-add $KEYS/cbb
		     REAL=$(cat /usr/lib/cryptobone/keys/real.key) 2> /dev/null
		fi
	   fi    
           
           # remove the boot file system
	   umount /dev/mapper/cryptobone
	   cryptsetup remove cryptobone
	   losetup -d $L
    
           # mount the permanent file system
	   L=$(losetup -f)
	   losetup $L $KEYS/permanent.fs
           mount $L $KEYS
	   chmod 700 $KEYS

           echo 9999 > $SWITCH
           chattr +i $SWITCH
	   log_debug "secrets become invisible"

    	   if [ ! -L /usr/lib/cryptobone/ALLINONE ]
	   then
	        # we're using a real Crypto Bone
		log_debug "Trying to contact the external device"
	        if [ -f /usr/lib/cryptobone/cbb.config ]
	        then
	             chmod 600 /usr/lib/cryptobone/cbb.config
	             . /usr/lib/cryptobone/cbb.config
		     log_debug "reading the config file cbb.config"
                     # wait for BONEIP to come up
		     typeset -i count=0
		     while [ $count -lt 20 ]
                     do
		         log_debug "waiting for the external device to ping"
		         /bin/sleep 2
			 count=$count+1
                         /bin/ping -w1 -c1 ${BONEIP} 2>/dev/null > /dev/null
			 if [ $? -eq 0 ]
			 then
                             count=21
			 fi
                     done

                     /bin/ping -w1 -c1 ${BONEIP} 2>/dev/null > /dev/null
		     if [ $? -eq 0 ]
	             then
                          # upload the master key to the real crypto bone
			  log_debug "external device is reachable"
		          echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM UPLOAD $(echo $REAL) | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol
                          log_debug "master key sent"

		          count=0
		          while [ $count -lt 8 ]
                          do
		               /bin/sleep 2
			       count=$count+1
                               /bin/ping -w1 -c1 ${BONEIP} 2>/dev/null > /dev/null
	                  
			       RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) STATUS | /usr/bin/ssh -l cryptobone ${BONEIP} /cbb/cbcontrol)
  	                       if [ "${RES}x" = "activex" ]
	                       then 
			            log_debug "device becomes active"
				    echo "EXTERNAL is active now."
		                    touch /usr/lib/cryptobone/UPLOADED
				    count=11
			            RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM RESTART | /usr/bin/ssh -l cryptobone ${BONEIP} /cbb/cbcontrol)

			       else
			            log_debug "device inactive [$RES]"
		                    RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM UPLOAD $(echo $REAL) | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol)
			            log_debug "uploading master key again [$RES]"

	                       fi
			       log_debug "$count" "sending master key"
			       echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM UPLOAD $(echo $REAL) >> /root/deb
			  done     
			  echo "EXTERNAL finished."
	                  REAL="000000000000000000000000000000000000000000"
	                  unset REAL

	             fi
	        fi
           else
	        echo "ALL-IN-ONE finished."
     	   fi
	   REAL="000000000000000000000000000000000000000000"
	   unset REAL
     fi
}


##########################################################################
stop() {
     if [ -d /dev/shm/RAM ]
     then
         /usr/lib/cryptobone/bin/savemessages
     fi
     killall cryptoboned
}


##########################################################################
case "$1" in
  start)
	start
	;;
  stop) 
	stop
	;;
  restart)
        echo "deliberately not implemented"
	;;
  status)
        echo "deliberately not implemented"
	;;
  force-reload)
        echo "deliberately not implemented"
        ;;
  *)
	echo "Usage: /usr/lib/cryptobone/init.d/cryptoboned {start|stop}"
	exit 1
esac

exit 0
