#!/usr/bin/bash

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

# use the check only under Fedora, it fails under Ubuntu
if [ -L /etc/fedora-release ]
then
     . /usr/lib/cryptobone/check
fi

##############################################################################
# This file is part of the CRYPTO BONE
# File     : cbcontrol 
# Version  : 2.0 (ALL-IN-ONE)
# License  : BSD-3-Clause
# Date     : 24 May 2025
# Contact  : Please send enquiries and bug-reports to innovation@senderek.ie
#
# Copyright (c) 2015-2025
#	Ralf Senderek, Ireland.  All rights reserved. (https://senderek.ie)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#	   This product includes software developed by Ralf Senderek.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND  ANY EXPRESS OR 
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  IMPLIED WARRANTIES 
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
# POSSIBILITY OF SUCH DAMAGE.
##############################################################################

# This script is the cornerstone of the Cryptobone software. It is run by the root user
# to access the database of secrets via the UNIX-socket and perform certain tasks
# that are requested by the GUI cryptobone2.
# The GUI (running under the ordinary user UID) sends commands to this script
# via the sudo mechanism and cbcontrol executes these special commands using either
# the cryptobone daemon on the same machine (ALL-IN-ONE mode) or sends the commands
# to an external device (the cryptoboneexternd daemon) via a dedicated ssh tunnel
# (EXTERNAL mode).
#
# The Cryptobone software separates the user's GUI from the working of the "engine"
# that takes care of encrypted message transport (via safewebdrop scripts) and decryption
# of incoming webdrop messages and attachment files using and updating AES keys that are
# provided by the cryptobone daemon and stored in the database by the daemon.
# All cleartext files (messages and attachments) reside inside a ramdisk that can be
# used by the root user only. The GUI needs to send the appropriate commands via sudo
# to this script to have the task completed with access to the ramdisk that is inaccessible
# to everyone except root.
#
# There are certain commands that are executed by cbcontrol directly (such as switching from
# ALL-IN-ONE mode to EXTERNAL mode and back, that need no secrets to be used.
# Other commands (like sending messages and attachments) need the current AES secret key for the
# intended recipient and either get these secrets via the UNIX-socket from the daemon,
# that has the secrets in memory or transfer decrypted information from the ramdisk into the GUI.
# A special case are attachment files (ie a PDF) that need to be read by helper programs under
# the user's UID. That means the GUI may request a copy of the attachment file being stored
# in the user's home directory to be of any use. In case of sending or receiving attachments
# with an external device, an upload or download of the file must be performed via the ssh tunnel
# by this script.
#
# A special command is COPY_EXTERNAL_SECRETS which is used for a special purpose when the
# external device is used to host the database of secrets (and does all the other heavy lifting)
# in EXTERNAL mode.
# Because some secrets which have been generated by the external device must be transferred into the
# local client machine's database (the external master key and the ssh private key for instance),
# this scripts copies those secrets into its own database first and on the next boot, the script
# that starts the cryptobone daemon puts these secrets into the boot.fs, so that the external
# master key can be uploaded to the external device during the boot process.

. /usr/lib/cryptobone/cbcontrol.functions

#-------------------------------------------------------------#


if [ ! -d /dev/shm/RAM ]
then
    /usr/lib/cryptobone/rc.local
fi

date +%s > /dev/shm/RAM/GUI 2> /dev/null

if [ x$1 = "xUSEALLINONE" ]
then
     ln -s /usr/lib/cryptobone/version /usr/lib/cryptobone/ALLINONE
     /usr/bin/cp /usr/lib/cryptobone/allinone.header /usr/lib/cryptobone/safewebdrop.header 
     ###exit 0
fi

if [ x$1 = "xUSECRYPTOBONE" ]
then
     /bin/rm -f /usr/lib/cryptobone/ALLINONE
     /usr/bin/cp /usr/lib/cryptobone/extern.header /usr/lib/cryptobone/safewebdrop.header 
     ###exit 0
fi

if [ x$1 = "xBONEIP" ]
then
     if [ x$2 != "x" ] ; then
          echo "BONEIP=$2" > /usr/lib/cryptobone/cbb.config
	  exit 0
     fi
fi

if [ x$1 = "xGETALLINONE" ]
then
     if [ -L /usr/lib/cryptobone/ALLINONE ]
     then
          # ALLINONE mode
          echo -n "ALLINONE "
	  if [ -f /usr/lib/cryptobone/database ]; then
               echo -n "active"
          else
	       echo -n "nodatabase"
	  fi
     else
          # EXTERNAL DEVICE mode
	  echo -n "EXTERNAL "
	  # did we copy the external secrets already?
	  SSHKEY=$(echo "get-element EXTERN.cbb"  | socat - UNIX-connect:/usr/lib/cryptobone/secrets.sock 2> /dev/null)
          if [ x$SSHKEY = "x" ]; then
               echo -n "unfinished"
	       exit 0
	  fi
	  # do we have a IP address?
          if [ -f /usr/lib/cryptobone/cbb.config ]
	  then
	       . /usr/lib/cryptobone/cbb.config
               # ist the external device responding to ping?
               if ! ping -c1 -w1 ${BONEIP} 2>/dev/null >/dev/null;  then
                    echo -n "offline : "
	       else
	            # External device is reachable
                    # is the external device already active ?
                    if [ -f /usr/lib/cryptobone/UPLOADED ] ; then
                         echo -n "active  : "
	            else
                         echo -n "waiting : "
		    fi
	       fi	    
	       echo -n ${BONEIP}
	  else
               echo -n "0.0.0.0"
	  fi
	  SSHKEY=$(echo "get-element EXTERN.cbb"  | socat - UNIX-connect:/usr/lib/cryptobone/secrets.sock 2> /dev/null)
          if [ x$SSHKEY = "x" ]; then
               echo -n "unfinished"
	  fi

     fi
     exit 0
fi

if [ x$1 = "xDELETE" ]; then
     if [ x$2 = "xCONFIG" ]; then
          /bin/rm /usr/lib/cryptobone/cbb.config
	  echo -n "success"
	  exit 0
     fi
fi

if [ x$1 = "xCOPY_EXTERNAL_SECRETS" ]
then
     if [ x$2 != "x" ]
     then
          DIR=$2
          if [ -f $DIR/master.key ]
	  then
	       # there are keys on the SD card
	       echo -n "replace EXTERN.OVERWRITE yes" | socat -t15 - UNIX-connect:$SOCK 2> /dev/null
               /bin/cp $DIR/local.key /usr/lib/cryptobone/keys/EXTERN.local.key 2>/dev/null
	       /bin/chown root /usr/lib/cryptobone/keys/EXTERN.local.key 2>/dev/null
	       /bin/chmod 600 /usr/lib/cryptobone/keys/EXTERN.local.key 2>/dev/null
	       if [ -f /usr/lib/cryptobone/keys/EXTERN.local.key ]; then
	            /usr/bin/sha256sum /usr/lib/cryptobone/keys/EXTERN.local.key | /usr/bin/cut -c-64 > /usr/lib/cryptobone/keys/EXTERN.local.key.sha256
	            /bin/chmod 600 /usr/lib/cryptobone/keys/EXTERN.local.key.sha256
	       else	    
	            echo -n "failed"
	            exit 2
               fi 

               # write 2 secrets directly to database 
	       LINE=$(/bin/cat $DIR/master.key | cut -c-40)
	       echo -n "replace EXTERN.real.key $LINE" | socat -t15 - UNIX-connect:$SOCK 2> /dev/null
         
               RES=$(echo  "get-element EXTERN.real.key" | socat -t15 - UNIX-connect:$SOCK 2> /dev/null)
	       if [ x$RES != "x$LINE" ]
	       then
	            echo -n "failed"
	            exit 2
	       fi

	       # base64 encode cbb after it is decrypted to form one line          
	       /usr/bin/ssh-keygen -p -P $(/bin/cat $DIR/local.key) -N "" -f  $DIR/cbb 2> /dev/null > /dev/null
	       LINE=$(/bin/cat $DIR/cbb | base64 | tr -d "\n" )
	       echo "replace EXTERN.cbb $LINE" | socat -t15 - UNIX-connect:$SOCK 2> /dev/null
               RES=$(echo "get-element EXTERN.cbb" | socat -t15 - UNIX-connect:$SOCK 2> /dev/null)
	       if [ x$RES != "x$LINE" ]
	       then
	            echo -n "failed"
	            exit 2
	       fi
	       # reaching this point, all secrets have been transferred correctly.
	       echo -n "success"
	       exit 0
	  fi
     fi
     echo -n "nokeys"
     exit 1
fi


if [ -L /usr/lib/cryptobone/ALLINONE ]
then
     # process all commands for the ALLINONE mode
     if [ "x$1" = "xEXIT" ]
     then
          echo "Bye for now."
          exit 0
     fi


     # check if querying local.key works, compare with sha256 hashvalue
     # exit "failed", if local.key cannot be accessed (directly or via a second device)

     if [ ! -f /usr/lib/cryptobone/keys/local.key.sha256 ]
     then
          /usr/lib/cryptobone/rc.local
     fi

     TESTHASH=$(/bin/cat /usr/lib/cryptobone/keys/local.key.sha256 2>/dev/null | cut -c-64)
     HASH=$(/usr/lib/cryptobone/getlocalsecret | sha256sum | cut -c-64)
     
     if [ x$HASH != x$TESTHASH ]
     then
          echo "failed: local authentication"
          exit 1
     fi

     if [ $# -ge 1 ]
     then
          case $1 in

          ATTACHMENT)   case $2 in
	                      LIST)             attachmentlist
			                        ;;

			      COPY)             copy_attachment "$3"
			                        ;;

                              DELETE)           delete_attachment "$3"
			                        ;;

                        esac
			;;

	  EXTERNAL)     case $2 in
	  		     STATUS)		 check_external
			                         ;;
			esac     
	                ;;

          FETCH)        /usr/lib/cryptobone/systemd.fetch
                        ;;

          KEY)          case $2 in


                              CONTACT)		contact_registered "$3"
			                        ;;

			      NEWSECRETS) 	get_new_secrets
			                        ;;

			      RECIPIENTLIST)	recipient_list	
			                        ;;

			      #RESET)		reset_key_for_email "$3"
			      #                  ;;

		 	      USE) 	        register_new_key "$3"  "$4"
			                        ;;

                              *) 	        echo "unknown KEY command"
			                        ;;

                        esac
                        ;;
			      
          MESSAGE)      case $2 in
			      COPY)             copy_message "$3"
			                        ;;
			esac
			;;

          #NETWORK) 	case $2 in
	  # only for external device
	#		      STATUS)	case $3 in
        #                                    CONNECT)  /usr/bin/sudo /sbin/ifconfig  2>&1
	#						      ;;
	#				    FIREWALL) /usr/bin/sudo /usr/lib/cryptobone/firewall status
	#					      ;;
        #                                    PING)     /bin/ping -c1 -w1 $(cat /usr/lib/cryptobone/pinghost)
        #                                              ;;
        #                                esac
	#			esac
        #                ;;

          ###POWEROFF)     echo "going down"
          ###			/usr/lib/cryptobone/bin/cbb-poweroff 2>/dev/null
	  ###		;;
         
	  READ)	        case $2 in
			      DESTROY)		destroy_message $3
			                        ;;

			      MESSAGELIST)	get_message_list
			                        ;;
			      
			      WEBDROPLIST)	get_webdrop_list
			                        ;;

                              MESSAGE)		read_message $3
			                        ;;
                              
			      WEBDROP)		read_webdrop $3
			                        ;;
                        esac
			;;


          RESET)	echo "reset the masterkey"
			;;

	  SETUP)        case $2 in
			     WID)	     get_wid ;;
			     WEBDROP)        show_safewebdrop_setup ;;
			     WEBDROPSECRET)  setup_safewebdropsecret "$3" ;;
			     WEBDROPSERVER)  setup_safewebdropserver  "$3" ;;
			     WEBDROPUSER)    setup_safewebdropuser "$3" ;;
			     CLEARREG)       setup_clear_safewebdrop_registration ;;
			     REGISTER)       setup_register_safewebdrop ;;
			     REGISTRATION)   setup_get_safewebdrop_registration ;;
			esac
			;;

          SHOW)         echo "forbidden";
	                ;;

          STATUS)       if  echo "get-element cryptobone" | socat -t15 - UNIX-connect:$SOCK > /dev/null
		        then 
			     echo "active"
		        else 
			     echo "waiting"
		        fi
		        ;;

	  SYSTEM)       case $2 in
	                     SUSPEND)    cryptobonesuspend ;;
	  		     RESUME)     cryptoboneresume ;;
	  		     POWEROFF)   clear_RAM ;;
          		     RESTART)    /usr/lib/cryptobone/rc.local ;;
	 		esac
	  		;;

          
	  WEBDROP)      safewebdrop_message "$2"  "$3" "$4"
			;;


          *)            echo "failed"
		        ;;
         esac
         exit 0
     fi
     echo "failed"
     exit 1

else
     # real Crypto Bone
     # send all commands via ssh tunnel to $BONEIP using cryptobone's ssh key

     export SSH_AUTH_SOCK=/usr/lib/cryptobone/ssh.sock	  
     if [ -f /usr/lib/cryptobone/cbb.config ]
     then
	  /bin/chmod 600 /usr/lib/cryptobone/cbb.config
          . /usr/lib/cryptobone/cbb.config 2> /dev/null
     else
          BONEIP="none"
	  echo "BONEIP MISSING"
	  exit 0
     fi 

     # check if external device is active or not

     if [ ! -f /usr/lib/cryptobone/UPLOADED ]; then

          # ist the external device responding to ping?
          if ping -c1 -w1 ${BONEIP} 2>/dev/null >/dev/null;  then
                echo "master key not uploaded"
          else
                echo "offline"
		exit 0
          fi

          RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) STATUS | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol)
	  if  [[ $RES = "active" ]] ; then
               touch /usr/lib/cryptobone/UPLOADED
	  fi
	  if  [[ $RES = "waiting" ]] ; then
	       # the external database cannot be accessed
	       echo "master key not available"
               /usr/bin/rm -f /usr/lib/cryptobone/UPLOADED 2>/dev/null
	       exit 0
	  fi
     fi

     if [ -f /usr/lib/cryptobone/UPLOADED ]; then
          # external device is active
          # if sending attachments with an external crypto bone, then upload the attachment before the
          # WEBDROP command safewebdrop_message "$2"  "$3" "$4" is handed over to the external device
          if [ $1 = "WEBDROP" ] ; then
               if [ $4 != "none" ]; then
                    echo $(/usr/lib/cryptobone/getlocalsecret) ATTACH $(/usr/bin/cat $4 | base64 --wrap 0) | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol
               fi
          fi


          # if downloading messages from the external crypto bone, exit after writing the result to the user's homedir.
	  if [ $1 = "MESSAGE" ] ; then

               if [ $2 == "COPY" ]; then
                    # find the user's home directory
                    USER=$(cat /etc/sudoers.d/cbcontrol | tail -1 | cut -f1 -d " ") 
                    if [ ! -d /home/${USER}/.safewebdrop ] ; then
                         /usr/bin/mkdir /home/${USER}/.safewebdrop 2> /dev/null
                         chmod 700 /home/${USER}/.safewebdrop
                         chown ${USER} /home/${USER}/.safewebdrop
                    fi      
		    # TODO check if $3 exists. Change name or overwrite $3.b64
                    TEMP=/home/${USER}/.safewebdrop/$3.b64
                    DEST=/home/${USER}/.safewebdrop/$3
                    echo $(/usr/lib/cryptobone/getlocalsecret) MESSAGE COPY $3  | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol > ${TEMP}
                    if [ $? -eq 0 ] ; then  
                         /usr/bin/base64 -d ${TEMP} > ${DEST}
                         if [ $? -eq 0 ] ; then 
		              /usr/bin/rm -f ${TEMP}	      
			      echo "message $1 SAVED"
			 fi     
                    fi
                    chown ${USER} /home/${USER}/.safewebdrop/*
                    chmod 600 /home/${USER}/.safewebdrop/*
                    exit 0
               fi
          fi

          # if downloading attachments from the external crypto bone, exit after writing the result to the user's homedir.
	  if [ $1 = "ATTACHMENT" ] ; then

               if [ $2 == "COPY" ]; then
                    # find the user's home directory
                    USER=$(cat /etc/sudoers.d/cbcontrol | tail -1 | cut -f1 -d " ") 
                    if [ ! -d /home/${USER}/.safewebdrop ] ; then
                         /usr/bin/mkdir /home/${USER}/.safewebdrop 2> /dev/null
                         chmod 700 /home/${USER}/.safewebdrop
                         chown ${USER} /home/${USER}/.safewebdrop
                    fi      
                    TEMP=/home/${USER}/.safewebdrop/$3.b64
                    DEST=/home/${USER}/.safewebdrop/$3
                    echo $(/usr/lib/cryptobone/getlocalsecret) ATTACHMENT COPY $3  | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol > ${TEMP}
                    if [ $? -eq 0 ] ; then  
                         /usr/bin/base64 -d ${TEMP} > ${DEST}
                         if [ $? -eq 0 ] ; then 
		              /usr/bin/rm -f ${TEMP}	      
			      echo "$1 SAVED"
			 fi     
                    fi
                    chown ${USER} /home/${USER}/.safewebdrop/*
                    chmod 600 /home/${USER}/.safewebdrop/*
                    exit 0
               fi
          fi


          # send the commands directly to the EXTERNAL Crypto Bone
          echo $(/usr/lib/cryptobone/getlocalsecret) $1 $2 $3 $4 | /usr/bin/ssh -l cryptobone  ${BONEIP} /cbb/cbcontrol      
          if [ $1 = "POWEROFF" ]
          then
               # shutdown of an active system has finished, record shutdown here.
               /bin/rm /usr/lib/cryptobone/UPLOADED 2> /dev/null
          fi	  
     else	  
          # external device is not yet active or unreachable
	  if [ $BONEIP != "none" ] 
	  then
	       # we have cbb.config, the external crypto bone may be reachable
	       if ping -c1 -w1 ${BONEIP} 2>/dev/null >/dev/null
	       then
	            echo "master key not available"
               else
	            echo "offline"
	       fi
	       exit 0
	       
          else
	       # we don't have any configuration yet
               # external crypto bone is not reachable
	       echo "no IP address"
	       exit 0
	  fi     
     fi	  
fi

#########################################################
