cryptonas-branches/pythonrewrite/bin/CryptoBox.py
lars 0fe6d426ed added mounting and unmounting of config partition
moved config partition handling to CryptoBoxSettings
implemented environment checks (writeable config, https (off for now))
chown mounted directory after mount to the cryptobox user
2006-11-03 14:27:19 +00:00

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()