#!/usr/bin/python
# *-* coding:utf-8 *-*

#***************************************************************************
# This file is part of the CRYPTO BONE
# File     : external-cryptobone-admin  (installed in /usr/bin)
# Version  : 1.1.0 (external cryptobone)
# License  : BSD
# Date     : Saturday, 5 November 2016
# Contact  : Please send enquiries and bug-reports to innovation@senderek.ie
#
#
# Copyright (c) 2015-2017
#	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.
#****************************************************************************

import os
import base64
import time

OS = os.name

###############################################################
def unix (command) :
     if OS == "posix":
          Pipe = os.popen(command, "r")
          Result = Pipe.read()
          Pipe.close()
     return Result

###############################################################
	 
DBfile  = "/usr/lib/cryptobone/ext/database"
Keyfile = "/usr/lib/cryptobone/ext/master.key"
SSHDfile= "/usr/lib/cryptobone/ext/init.d/sshd_config-external"
RED     = "#ffcccc"
GREEN   = "#ccffcc"
GRAY    = "#ccc"

GUI = False
try:
    from Tkinter import *
    from tkMessageBox import *
    import tkFont
    GUI = True
except:
    pass

X11 = False
PID1=unix("pidof Xwayland")
PID2=unix("pidof Xorg")
if ( ("x"+PID1 != "x") or ("x"+PID2 != "x")):
     X11=True


###############################################################
def get_status():
     global ACTIVE, DB, FIREWALL, REMOTE, SSHD

     RES=unix("systemctl is-enabled cryptoboneexternd")
     if "enabled" in RES :
          ACTIVE = True
	  EnableButton.configure(text="Disable External Crypto Bone")
          RES=unix("systemctl is-active cryptoboneexternd")
          if "xactive" in "x"+RES:
	        ActiveLabel.configure(text="This external Crypto Bone is enabled and running.")
	        ActiveLabel.configure(bg=GREEN)
          else:		
	        if "failed" in RES:
		     ActiveLabel.configure(text="This external Crypto Bone is enabled but failed to start.")
	             ActiveLabel.configure(bg=GRAY)
		else:     
		     ActiveLabel.configure(text="This external Crypto Bone is enabled but not running.")
	             ActiveLabel.configure(bg=GRAY)
		
     else:
          ACTIVE = False
	  EnableButton.configure(text="Enable External Crypto Bone")
	  ActiveLabel.configure(text="The external Crypto Bone is not enabled on this machine.")
	  ActiveLabel.configure(bg=GRAY)


     print "Trying to find the encrypted data base file  ..."
     RES=unix("beesu -c \"ls /usr/lib/cryptobone/ext/database 2>/dev/null \"")
     if "database" in RES:
          DB = True
	  DBLabel.configure(text="The encrypted data base is initialized.")
	  DBLabel.configure(bg=GREEN)
     else:	  
          DB = False
	  DBLabel.configure(text="The encrypted data base is missing.")
	  DBLabel.configure(bg=GRAY)

     RES=unix("systemctl is-active firewalld")
     if "xactive" in "x"+RES :
          FIREWALL = False
	  FWButton.configure(text="Activate Restrictive Firewall")
          FirewallLabel.configure(text="The system's original firewall daemon is active.")
	  FirewallLabel.configure(bg=GRAY)
     else:		
          # firewalld is inactive, check the existence of usefirewall
	  print "Trying to find out if the Crypto Bone's firewall is requested ..."
          RES=unix("beesu -c \"ls /usr/lib/cryptobone/ext/init.d/usefirewall 2>/dev/null \"")
	  if "usefirewall" in RES:
	       FIREWALL = True
	       FWButton.configure(text="Return To Original Firewall Daemon")
               FirewallLabel.configure(text="The Crypto Bone's restrictive firewall is active.")
	       FirewallLabel.configure(bg=GREEN)
	  else:
	       # custom or no firewall
               FirewallLabel.configure(text="The Crypto Bone's restrictive firewall is not active.")
	       FirewallLabel.configure(bg=GRAY)

     RES=unix("systemctl is-enabled sshd 2>&1")
     if "Failed to get unit" in RES :
          SSHD = False
	  SSHDButton.configure(text="Install Secure Shell Daemon")
          RemoteLabel.configure(text="Secure Shell Daemon is not installed but needed.")
	  RemoteLabel.configure(bg=RED)
     else:		
          # sshd is installed
	  SSHD = True
          RES=unix("systemctl is-active sshd 2>&1")
          if ("inactive" in RES) or ("unknown" in RES) :
               RemoteLabel.configure(text="The Secure Shell Daemon is not running.")
	       RemoteLabel.configure(bg=RED)
	       showinfo( "ERROR","Please enable the secure shell daemon.")
	  elif "xactive" in "x"+RES:     
               print "Trying to find the active sshd configuration ...\n"
               RES=unix("beesu -c \"diff /etc/ssh/sshd_config " + SSHDfile + " 2>&1\"")
               if not RES :
                    REMOTE = True
	            SSHDButton.configure(text="Return To Original SSHD Configuration")
                    RemoteLabel.configure(text="The system's secure shell daemon is hardened.")
	            RemoteLabel.configure(bg=GREEN)
               else:
                    REMOTE = False
	            SSHDButton.configure(text="Harden Secure Shell Daemon")
                    RemoteLabel.configure(text="The system's secure shell daemon is untouched.")
	            RemoteLabel.configure(bg=GRAY)


###############################################################
def terminate_GUI():
     Window.destroy()     


###############################################################
def OpenHelpUrl():
     import webbrowser
     webbrowser.open_new("https://crypto-bone.com/help")
     
###############################################################
def activate_external():
     global ACTIVE
     if not ACTIVE:
          print "Enabling cryptoboneexternd ..."
	  print "This takes about half a minute ..."
	  RES=unix("df | grep BOOT")
	  if not "BOOT" in RES:
	       showinfo( "ERROR","There is no USB partition with a BOOT label which is needed to store secrets for the main machine.\nPlease insert a  USB key labelled BOOT and enable again.")
	       return
          print unix("beesu -c \"systemctl enable cryptoboneexternd; systemctl start cryptoboneexternd\"")
	  # check the result
	  RES=unix("beesu -c \"ls /usr/lib/cryptobone/ext/masterkey\"")
	  if "masterkey" in RES:
	       showinfo( "SUCCESS","The external cryptobone daemon on this machine is initialised.\n\nPlease transfer the new secrets to your main machine now, if you have enabled the external Crypto Bone for the first time.")
	  else:
	       showinfo( "ERROR","New secrets have been created but there is no USB partition to write to. Please insert a  USB key labelled BOOT and reboot this machine to initialise again.")
	  ACTIVE = True
     else:
          print "Disabling cryptoboneexternd ..."
          print unix("beesu -c \"systemctl disable cryptoboneexternd; systemctl stop cryptoboneexternd\"")
	  ACTIVE = False
     get_status()

###############################################################
def harden_firewall():
     global FIREWALL
     if not FIREWALL:
          print "Activating the restrictive firewall for the external Crypto Bone ..."
          showinfo( "ATTENTION","If you activate the restrictive firewall this machine cannot be used as a general purpose computer with access to the internet.\n\nThe restrictive firewall replaces any firewall setting after booting this machine")
          print unix("beesu -c \"touch /usr/lib/cryptobone/ext/init.d/usefirewall\"")
	  FIREWALL = True
          showinfo( "REBOOT","You need to reboot your computer to ensure that this change takes effect.")
     else:
          print "Restoring the original firewalld configuration  ..."
          print unix("beesu -c \"rm -f /usr/lib/cryptobone/ext/init.d/usefirewall ; systemctl start firewalld\"")
	  FIREWALL = False
     if not ACTIVE:
          showinfo( "ERROR","You also need to activate the external cryptobone daemon on this machine.")
     get_status()

###############################################################
def harden_sshd():
     global REMOTE
     if not SSHD:
          install_sshd()
     else:	  
          if not REMOTE:
               print "Hardening the secure shell daemon for the external Crypto Bone ..."
               print unix("beesu -c \"systemctl stop sshd; cp /etc/ssh/sshd_config /usr/lib/cryptobone/ext/init.d/sshd_config-original; cp /usr/lib/cryptobone/ext/init.d/sshd_config-external /etc/ssh/sshd_config; chmod 600 /etc/ssh/sshd_config; systemctl start sshd\"")
	       REMOTE = True
          else:
               print "Restoring the original secure shell daemon configuration ..."
               print unix("beesu -c \"systemctl stop sshd; cp /usr/lib/cryptobone/ext/init.d/sshd_config-original /etc/ssh/sshd_config; chmod 600 /etc/ssh/sshd_config; systemctl start sshd\"")
	       REMOTE = False
     get_status()

###############################################################
def install_sshd():
     global SSHD 
     
     Info = """You are about to install the secure shell daemon on this machine.

You will then be able to access this machine from your main computer through the network interface.

Do you want to install the Secure Shell Daemon now?
"""
     if askyesno('SSHD Installation', Info):
          print "INSTALLING SSH DAEMON" 
          print unix("beesu -c \"dnf install openssh-server -y; systemctl enable sshd; systemctl start sshd; systemctl status sshd\"")
          SSHD = True
          
###############################################################
def reset_cryptobone():
     Info = """You are about to destroy the EXTERNAL Crypto Bone on this machine.

After completing this step the encryption key database and all access information for the EXTERNAL Crypto Bone on this machine will be destroyed.
While the external cryptobone software will still be there, you will return to square one and this machine will try to create new secrets on the next boot.

Do you want to remove all EXTERNAL Crypto Bone data now?
"""
     if askyesno('Destroying an External Crypto Bone', Info):
          print "DESTROYING AN EXTERNAL CRYPTO BONE" 
          print unix("beesu -c \"echo yes | /usr/lib/cryptobone/ext/reset\"")
     
###############################################################
# Main
###############################################################

ACTIVE=False
DB=False
FIREWALL=False
REMOTE=False
SSHD=False


print "This is the administration tool for the EXTERNAL Crypto Bone on this machine."
print 
print "In order to collect some of the status information root permission is needed."
print "You may be prompted for the super user (root) password several times."
print


if GUI and X11:

     Window = Tk()
     Window.title("External Crypto Bone Administration")
     Window.geometry('960x680')

     MainFrame = Frame(Window)
     TopFrame = Frame(MainFrame, pady=10)
     StatusFrame = Frame(MainFrame, pady=5)
     BottomFrame = Frame(Window, pady=5)
     

     Big = tkFont.Font(family="utopia", size=16)
     Title = tkFont.Font(family="utopia", size=12)
     Info  = tkFont.Font(family="arial", size=10, slant="italic")
     Bold  = tkFont.Font(family="arial", size=11, weight="bold")
     Normal  = tkFont.Font(family="arial", size=10, weight="normal")
     BFont = tkFont.Font(family="utopia", size=12, weight="normal")
     TextFont = tkFont.Font(family="arial", size=11, weight="normal")

     INFO = """
The Crypto Bone has two different modes of operation that can be selected in the graphical user interface. 

In its default mode (ALL-IN-ONE) the GUI (cryptobone) uses the internal, encrypted data base
on the main machine through a UNIX socket on the same computer. 

In EXTERNAL mode, a separate device is used to store the data base and the main machine 
establishes contact to this second external device via a secure shell link.

You are now turning this machine into a separate, EXTERNAL Crypto Bone.

Do you wish to do this?  
(for more information: man external-cryptobone-admin and https://crypto-bone.com)

     """


     # Status
     BoneLabel = Label(TopFrame, text="EXTERNAL CRYPTO BONE  1.1 (Administration)", font=Big, width=50)
     BoneLabel.bind("<Button-1>",lambda e: OpenHelpUrl())
     ActiveLabel = Label(StatusFrame, text="no information", width=60, height=1, font=Bold, bg="#ccc")
     DBLabel = Label(StatusFrame, text="no information", width=60, height=1, font=Bold, bg="#ccc")
     FirewallLabel = Label(StatusFrame, text="no information", width=60, height=1, font=Bold, bg="#ccc")
     RemoteLabel = Label(StatusFrame, text="no information", width=60, height=1, font=Bold, bg="#ccc")
     InfoLabel = Label(StatusFrame, text=INFO, width=100, height=14, font=Normal, bg="#eee")
     BoneLabel.pack(side=LEFT)
     ActiveLabel.pack(fill=Y)
     DBLabel.pack(fill=Y)
     FirewallLabel.pack(fill=Y)
     RemoteLabel.pack(fill=Y)
     InfoLabel.pack(fill=Y, pady=20)

     SSHDButton = Button(master=BottomFrame, text="Harden Secure Shell Daemon", width=40, bg="#ccc", font=BFont, command=harden_sshd)
     RESETButton = Button(master=BottomFrame, text="Forget Everything", width=40, bg="#fcc", font=BFont, command=reset_cryptobone)

     FWButton = Button(master=BottomFrame, text="Activate Restrictive Firewall", width=40, bg="#ccc", font=BFont, command=harden_firewall)
     EnableButton = Button(master=BottomFrame, text="Enable External Crypto Bone", width=40, bg="#ccc", font=BFont, command=activate_external)
     ExitButton = Button(master=BottomFrame, text="EXIT", width=20, bg="#cce", font=BFont, command=terminate_GUI)
     
     EnableButton.pack(pady=8)
     FWButton.pack(pady=8)
     SSHDButton.pack(pady=8)
     RESETButton.pack(pady=8)
     ExitButton.pack(pady=20)

     TopFrame.pack()
     StatusFrame.pack()
     MainFrame.pack()
     BottomFrame.pack()
     
     get_status() 

     Window.mainloop()
else:
     print "TEXT VERSION"
##############################################################
