lars
0fe6d426ed
moved config partition handling to CryptoBoxSettings implemented environment checks (writeable config, https (off for now)) chown mounted directory after mount to the cryptobox user
266 lines
8.2 KiB
Python
Executable file
266 lines
8.2 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.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 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()
|
|
|