276 lines
8.4 KiB
Python
Executable file
276 lines
8.4 KiB
Python
Executable file
#!/usr/bin/env python2.4
|
|
'''
|
|
This is the web interface for a fileserver managing encrypted filesystems.
|
|
|
|
It was originally written in bash/perl. Now a complete rewrite is in
|
|
progress. So things might be confusing here. Hopefully not for long.
|
|
:)
|
|
'''
|
|
|
|
# check python version
|
|
import sys
|
|
(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info
|
|
if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)):
|
|
sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version)
|
|
sys.exit(1)
|
|
|
|
import CryptoBoxContainer
|
|
from CryptoBoxExceptions import *
|
|
import re
|
|
import os
|
|
import CryptoBoxTools
|
|
import subprocess
|
|
|
|
|
|
|
|
class CryptoBox:
|
|
'''this class rules them all!
|
|
|
|
put things like logging, conf and oter stuff in here,
|
|
that might be used by more classes, it will be passed on to them'''
|
|
|
|
VERSION = "0.3~1"
|
|
|
|
def __init__(self, config_file=None):
|
|
import CryptoBoxSettings
|
|
self.log = self.__getStartupLogger()
|
|
self.prefs = CryptoBoxSettings.CryptoBoxSettings(config_file)
|
|
self.__runTests()
|
|
|
|
|
|
def __getStartupLogger(self):
|
|
import logging
|
|
'''initialises the logging system
|
|
|
|
use it with: 'self.log.[debug|info|warning|error|critical](logmessage)'
|
|
all classes should get the logging instance during __init__:
|
|
self.log = logging.getLogger("CryptoBox")
|
|
|
|
first we output all warnings/errors to stderr
|
|
as soon as we opened the config file successfully, we redirect debug output
|
|
to the configured destination'''
|
|
## basicConfig(...) needs python >= 2.4
|
|
try:
|
|
log_handler = logging.getLogger("CryptoBox")
|
|
logging.basicConfig(
|
|
format='%(asctime)s CryptoBox %(levelname)s: %(message)s',
|
|
stderr=sys.stderr)
|
|
log_handler.setLevel(logging.ERROR)
|
|
log_handler.info("loggingsystem is up'n running")
|
|
## from now on everything can be logged via self.log...
|
|
except:
|
|
raise CBEnvironmentError("couldn't initialise the loggingsystem. I give up.")
|
|
return log_handler
|
|
|
|
|
|
# do some initial checks
|
|
def __runTests(self):
|
|
self.__runTestUID()
|
|
self.__runTestRootPriv()
|
|
|
|
|
|
def __runTestUID(self):
|
|
if os.geteuid() == 0:
|
|
raise CBEnvironmentError("you may not run the cryptobox as root")
|
|
|
|
|
|
def __runTestRootPriv(self):
|
|
"""try to run 'super' with 'CryptoBoxRootActions'"""
|
|
try:
|
|
devnull = open(os.devnull, "w")
|
|
except IOError:
|
|
raise CBEnvironmentError("could not open %s for writing!" % os.devnull)
|
|
try:
|
|
prog_super = self.prefs["Programs"]["super"]
|
|
except KeyError:
|
|
raise CBConfigUndefinedError("Programs", "super")
|
|
try:
|
|
prog_rootactions = self.prefs["Programs"]["CryptoBoxRootActions"]
|
|
except KeyError:
|
|
raise CBConfigUndefinedError("Programs", "CryptoBoxRootActions")
|
|
try:
|
|
proc = subprocess.Popen(
|
|
shell = False,
|
|
stdout = devnull,
|
|
stderr = devnull,
|
|
args = [prog_super, prog_rootactions, "check"])
|
|
except OSError:
|
|
raise CBEnvironmentError("failed to execute 'super' (%s)" % self.prefs["Programs"]["super"])
|
|
proc.wait()
|
|
if proc.returncode != 0:
|
|
raise CBEnvironmentError("failed to call CryptoBoxRootActions (%s) via 'super' - maybe you did not add the appropriate line to /etc/super.tab?" % prog_rootactions)
|
|
|
|
|
|
# this method just demonstrates inheritance effects - may be removed
|
|
def cbx_inheritance_test(self, string="you lucky widow"):
|
|
self.log.info(string)
|
|
|
|
|
|
# RFC: why should CryptoBoxProps inherit CryptoBox? [l]
|
|
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
|
|
class CryptoBoxProps(CryptoBox):
|
|
'''Get and set the properties of a CryptoBox
|
|
|
|
This class contains all available devices that may be accessed.
|
|
All properties of the cryptobox can be accessed by this class.
|
|
'''
|
|
|
|
def __init__(self, config_file=None):
|
|
'''read config and fill class variables'''
|
|
CryptoBox.__init__(self, config_file)
|
|
self.reReadContainerList()
|
|
|
|
|
|
def reReadContainerList(self):
|
|
self.log.debug("rereading container list")
|
|
self.containers = []
|
|
for device in CryptoBoxTools.getAvailablePartitions():
|
|
if self.isDeviceAllowed(device) and not self.isConfigPartition(device):
|
|
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self))
|
|
## sort by container name
|
|
self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1)
|
|
|
|
|
|
def isConfigPartition(self, device):
|
|
proc = subprocess.Popen(
|
|
shell = False,
|
|
stdout = subprocess.PIPE,
|
|
args = [
|
|
self.prefs["Programs"]["blkid"],
|
|
"-c", os.path.devnull,
|
|
"-o", "value",
|
|
"-s", "LABEL",
|
|
device])
|
|
(output, error) = proc.communicate()
|
|
return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"]
|
|
|
|
|
|
def isDeviceAllowed(self, devicename):
|
|
"check if a device is white-listed for being used as cryptobox containers"
|
|
import types
|
|
allowed = self.prefs["Main"]["AllowedDevices"]
|
|
if type(allowed) == types.StringType: allowed = [allowed]
|
|
for a_dev in allowed:
|
|
"remove double dots and so on ..."
|
|
real_device = os.path.realpath(devicename)
|
|
if a_dev and re.search('^' + a_dev, real_device): return True
|
|
return False
|
|
|
|
|
|
def getLogData(self, lines=None, maxSize=None):
|
|
"""get the most recent log entries of the cryptobox
|
|
|
|
the maximum number and size of these entries can be limited by 'lines' and 'maxSize'
|
|
"""
|
|
# return nothing if the currently selected log output is not a file
|
|
try:
|
|
if self.prefs["Log"]["Destination"].upper() != "FILE": return []
|
|
log_file = self.prefs["Log"]["Details"]
|
|
except KeyError:
|
|
self.log.error("could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details")
|
|
return []
|
|
try:
|
|
fd = open(log_file, "r")
|
|
if maxSize: fd.seek(-maxSize, 2) # seek relative to the end of the file
|
|
content = fd.readlines()
|
|
fd.close()
|
|
except IOError:
|
|
self.log.warn("failed to read the log file (%s)" % log_file)
|
|
return []
|
|
if lines: content = content[-lines:]
|
|
content.reverse()
|
|
return content
|
|
|
|
|
|
def getContainerList(self, filterType=None, filterName=None):
|
|
"retrieve the list of all containers of this cryptobox"
|
|
try:
|
|
result = self.containers[:]
|
|
if filterType != None:
|
|
if filterType in range(len(CryptoBoxContainer.Types)):
|
|
return [e for e in self.containers if e.getType() == filterType]
|
|
else:
|
|
self.log.info("invalid filterType (%d)" % filterType)
|
|
result.clear()
|
|
if filterName != None:
|
|
result = [e for e in self.containers if e.getName() == filterName]
|
|
return result
|
|
except AttributeError:
|
|
return []
|
|
|
|
|
|
def getContainer(self, device):
|
|
"retrieve the container element for this device"
|
|
all = [e for e in self.getContainerList() if e.device == device]
|
|
if all:
|
|
return all[0]
|
|
else:
|
|
return None
|
|
|
|
|
|
def setNameForUUID(self, uuid, name):
|
|
"assign a name to a uuid in the ContainerNameDatabase"
|
|
used_uuid = self.getUUIDForName(name)
|
|
"first remove potential conflicting uuid/name combination"
|
|
if used_uuid:
|
|
## remember the container which name was overriden
|
|
for e in self.containers:
|
|
if e.getName() == name:
|
|
forcedRename = e
|
|
break
|
|
del self.prefs.nameDB[used_uuid]
|
|
self.prefs.nameDB[uuid] = name
|
|
self.prefs.nameDB.write()
|
|
## rename the container that lost its name (necessary while we use cherrypy)
|
|
if used_uuid:
|
|
## this is surely not the best way to regenerate the name
|
|
dev = e.getDevice()
|
|
old_index = self.containers.index(e)
|
|
self.containers.remove(e)
|
|
self.containers.insert(old_index, CryptoBoxContainer.CryptoBoxContainer(dev,self))
|
|
## there should be no reason for any failure
|
|
return True
|
|
|
|
|
|
def getNameForUUID(self, uuid):
|
|
"get the name belonging to a specified key (usually the UUID of a fs)"
|
|
try:
|
|
return self.prefs.nameDB[uuid]
|
|
except KeyError:
|
|
return None
|
|
|
|
|
|
def getUUIDForName(self, name):
|
|
""" get the key belonging to a value in the ContainerNameDatabase
|
|
this is the reverse action of 'getNameForUUID' """
|
|
for key in self.prefs.nameDB.keys():
|
|
if self.prefs.nameDB[key] == name: return key
|
|
"the uuid was not found"
|
|
return None
|
|
|
|
|
|
def removeUUID(self, uuid):
|
|
if uuid in self.prefs.nameDB.keys():
|
|
del self.prefs.nameDB[uuid]
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def getAvailableLanguages(self):
|
|
'''reads all files in path LangDir and returns a list of
|
|
basenames from existing hdf files, that should are all available
|
|
languages'''
|
|
languages = [ f.rstrip(".hdf")
|
|
for f in os.listdir(self.prefs["Locations"]["LangDir"])
|
|
if f.endswith(".hdf") ]
|
|
if len(languages) < 1:
|
|
self.log.error("No .hdf files found! The website won't render properly.")
|
|
return languages
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cb = CryptoBoxProps()
|
|
|