cryptonas-branches/pythonrewrite/bin2/CryptoBox.py

229 lines
7.4 KiB
Python

#!/usr/bin/env python
'''
This is a secure fileserver with encrypted filesystem and webinterface.
It was originally written in bash/perl. Now a complete rewrite is in
progress. So things might be confusing here. Hopefully not for long.
:)
'''
import CryptoBoxLogger
import CryptoBoxContainer
import CryptoBoxPreferences
import re
import os
CONFIG_FILE = "cbx.conf"
class CryptoBoxProps:
'''Get and set the properties of a CryptoBox
This class returns the available _and_ allowed devices, which will
be used further.
'''
def __init__(self):
'''read config and fill class variables'''
print CONFIG_FILE
self.cbxPrefs = CryptoBoxPreferences.Preferences(CONFIG_FILE)
self.debug = CryptoBoxLogger.CryptoBoxLogger(
self.cbxPrefs["debuglevel"],
self.cbxPrefs["debugfacility"],
self.cbxPrefs["logfile"] )
"TODO: move this to config file"
self.config = {}
self.config["ContainerNameDatabase"] = {
"4edc83f1-9ecd-4182-820f-b7815725a957":"Alpha",
"7dee9ef8-d69a-400b-8b74-33d828d8bea6":"Beta"}
self.containers = []
for device in self.__getAvailablePartitions():
if self.isDeviceAllowed(device):
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self, self.debug))
def debugMessage(self, level, text):
"print a debug message to the previously choosen debugging facility"
self.debug.printMessage(level,text)
def isDeviceAllowed(self, devicename):
"check if a device is white-listed for being used as cryptobox containers"
"TODO: broken!"
for a_dev in self.cbxPrefs["allowed_devices"]:
if a_dev and re.search('^' + a_dev, devicename): return True
return False
def getConfigValue(self, name):
"returns the value of a config setting"
try:
"TODO: return copy instead of original"
return self.cbxPrefs[name]
except KeyError:
return None
def setConfigValue(self, name, value):
"set the value of a config setting"
if not name: return False
try:
self.cbxPrefs[name] = value
except AttributeError:
self.cbxPrefs = {}
self.cbxPrefs[name] = value
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.logger.debugMessage(
CryptoBoxLogger.DebugLevels["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 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: del self.config["ContainerNameDatabase"][used_uuid]
self.config["ContainerNameDatabase"][uuid] = name
def getNameForUUID(self, uuid):
"get the name belonging to a specified key (usually the UUID of a fs)"
try:
return self.config["ContainerNameDatabase"][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.config["ContainerNameDatabase"].keys():
if self.config["ContainerNameDatabase"][key] == name: return key
"the uuid was not found"
return None
""" ************ internal stuff starts here *********** """
def __getAvailablePartitions(self):
"retrieve a list of all available containers"
ret_list = []
try:
"the following reads all lines of /proc/partitions and adds the mentioned devices"
fpart = open("/proc/partitions", "r")
try:
line = fpart.readline()
while line:
p_details = line.split()
if (len(p_details) == 4):
"the following code prevents double entries like /dev/hda and /dev/hda1"
(p_major, p_minor, p_size, p_device) = p_details
if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor):
p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
if p_parent == p_device:
if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]:
"major partition - its children are already in the list"
pass
else:
"major partition - but there are no children for now"
ret_list.append(p_device)
else:
"minor partition - remove parent if necessary"
if p_parent in ret_list: ret_list.remove(p_parent)
ret_list.append(p_device)
line = fpart.readline()
finally:
fpart.close()
return [self.__getAbsoluteDeviceName(e) for e in ret_list]
except IOError:
self.debugMessage(
"Could not read /proc/partitions",
CryptoBoxLogger.DebugLevels["warn"])
return []
def __getAbsoluteDeviceName(self, shortname):
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
this does also work for device mapper devices
if the result is non-unique, one arbitrary value is returned"""
if re.search('^/', shortname): return shortname
default = os.path.join("/dev", shortname)
if os.path.exists(default): return default
result = self.__findMajorMinorOfDevice(shortname)
"if no valid major/minor was found -> exit"
if not result: return default
(major, minor) = result
"for device-mapper devices (major == 254) ..."
if major == 254:
result = self.__findMajorMinorDeviceName("/dev/mapper", major, minor)
if result: return result[0]
"now check all files in /dev"
result = self.__findMajorMinorDeviceName("/dev", major, minor)
if result: return result[0]
return default
def __findMajorMinorOfDevice(self, device):
"return the major/minor numbers of a block device by querying /sys/block/?/dev"
if not os.path.exists(os.path.join("/sys/block", device)): return None
blockdev_info_file = os.path.join(os.path.join("/sys/block", device), "dev")
try:
f_blockdev_info = open(blockdev_info_file, "r")
blockdev_info = f_blockdev_info.read()
f_blockdev_info.close()
(str_major, str_minor) = blockdev_info.split(":")
"numeric conversion"
try:
major = int(str_major)
minor = int(str_minor)
return (major, minor)
except ValueError:
"unknown device numbers -> stop guessing"
return None
except IOError:
pass
def __findMajorMinorDeviceName(self, dir, major, minor):
"returns the names of devices with the specified major and minor number"
collected = []
try:
subdirs = [os.path.join(dir, e) for e in os.listdir(dir) if (not os.path.islink(os.path.join(dir, e))) and os.path.isdir(os.path.join(dir, e))]
"do a recursive call to parse the directory tree"
for dirs in subdirs:
collected.extend(self.__findMajorMinorDeviceName(dirs, major, minor))
"filter all device inodes in this directory"
collected.extend([os.path.realpath(os.path.join(dir, e)) for e in os.listdir(dir) if (os.major(os.stat(os.path.join(dir, e)).st_rdev) == major) and (os.minor(os.stat(os.path.join(dir, e)).st_rdev) == minor)])
result = []
for e in collected:
if e not in result: result.append(e)
return collected
except OSError:
return []
def __csv2list(self, csvstring):
'''transform a csv preferences string into a list'''
commalist = csvstring.split(",") # split the csv by ","
list = []
for element in commalist:
list.append(element.strip()) # remove whitespaces
return list