use ConfigObj for config file management

implement privilege dropping
test script added
This commit is contained in:
lars 2006-08-17 10:38:05 +00:00
parent 7443b4684e
commit 07a63dbd9c
6 changed files with 338 additions and 149 deletions

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
''' '''
This is a secure fileserver with encrypted filesystem and webinterface. This is the web interface for a fileserver managing encrypted filesystems.
It was originally written in bash/perl. Now a complete rewrite is in It was originally written in bash/perl. Now a complete rewrite is in
progress. So things might be confusing here. Hopefully not for long. progress. So things might be confusing here. Hopefully not for long.
@ -9,71 +9,80 @@ progress. So things might be confusing here. Hopefully not for long.
import CryptoBoxLogger import CryptoBoxLogger
import CryptoBoxContainer import CryptoBoxContainer
import CryptoBoxPreferences import configobj # to read and write the config file
import re import re
import os import os
import sys
import types
CONF_LOCATIONS = [
"./cryptobox.conf",
"~/.cryptobox.conf",
"/etc/cryptobox/cryptobox.conf"]
CONFIG_FILE = "cbx.conf"
class CryptoBoxProps: class CryptoBoxProps:
'''Get and set the properties of a CryptoBox '''Get and set the properties of a CryptoBox
This class returns the available _and_ allowed devices, which will This class contains all available devices that may be accessed.
be used further. All properties of the cryptobox can be accessed by this class.
''' '''
def __init__(self): def __init__(self, conf_file=None):
'''read config and fill class variables''' '''read config and fill class variables'''
print CONFIG_FILE if os.geteuid() != 0:
self.cbxPrefs = CryptoBoxPreferences.Preferences(CONFIG_FILE) sys.stderr.write("You need to be root to run this program!\n")
sys.exit(1)
if conf_file == None:
for f in CONF_LOCATIONS:
if os.path.exists(os.path.expanduser(f)):
conf_file = os.path.expanduser(f)
break
else:
sys.stderr.write("Could not find a configuration file. I give up.\n")
sys.exit(1)
try:
self.cbxPrefs = configobj.ConfigObj(conf_file)
except SyntaxError:
sys.stderr.write("Error during parsing of configuration file (%s).\n" % (conf_file, ))
sys.exit(1)
try:
nameDB_file = os.path.join(
self.cbxPrefs["Main"]["DataDir"],
self.cbxPrefs["Main"]["NameDatabase"])
if os.path.exists(nameDB_file):
self.nameDB = configobj.ConfigObj(nameDB_file, create_empty=True)
else:
self.nameDB = configobj.ConfigObj(nameDB_file)
except SyntaxError:
sys.stderr.write("Error during parsing of name database file (%s).\n" % (nameDB_file, ))
sys.exit(1)
self.__cboxUID = int(self.cbxPrefs["System"]["User"])
self.debug = CryptoBoxLogger.CryptoBoxLogger( self.debug = CryptoBoxLogger.CryptoBoxLogger(
self.cbxPrefs["debuglevel"], self.cbxPrefs["Log"]["Level"],
self.cbxPrefs["debugfacility"], self.cbxPrefs["Log"]["Facility"],
self.cbxPrefs["logfile"] ) self.cbxPrefs["Log"]["Destination"],
"TODO: move this to config file" self.__cboxUID)
self.config = {} self.dropPrivileges()
self.config["ContainerNameDatabase"] = { self.debugMessage = self.debug.printMessage
"4edc83f1-9ecd-4182-820f-b7815725a957":"Alpha",
"7dee9ef8-d69a-400b-8b74-33d828d8bea6":"Beta"}
self.containers = [] self.containers = []
for device in self.__getAvailablePartitions(): for device in self.__getAvailablePartitions():
if self.isDeviceAllowed(device): if self.isDeviceAllowed(device):
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self, self.debug)) self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self))
def debugMessage(self, level, text):
"print a debug message to the previously choosen debugging facility"
self.debug.printMessage(level,text)
def isDeviceAllowed(self, devicename): def isDeviceAllowed(self, devicename):
"check if a device is white-listed for being used as cryptobox containers" "check if a device is white-listed for being used as cryptobox containers"
"TODO: broken!" "TODO: broken!"
for a_dev in self.cbxPrefs["allowed_devices"]: allowed = self.cbxPrefs["Main"]["AllowedDevices"]
if type(allowed) == types.StringType: allowed = [allowed]
for a_dev in allowed:
if a_dev and re.search('^' + a_dev, devicename): return True if a_dev and re.search('^' + a_dev, devicename): return True
return False 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): def getContainerList(self, filterType=None, filterName=None):
"retrieve the list of all containers of this cryptobox" "retrieve the list of all containers of this cryptobox"
try: try:
@ -83,8 +92,7 @@ class CryptoBoxProps:
return [e for e in self.containers if e.getType() == filterType] return [e for e in self.containers if e.getType() == filterType]
else: else:
self.logger.debugMessage( self.logger.debugMessage(
CryptoBoxLogger.DebugLevels["info"], "info", "invalid filterType (%d)" % filterType)
"invalid filterType (%d)" % filterType)
result.clear() result.clear()
if filterName != None: if filterName != None:
result = [e for e in self.containers if e.getName() == filterName] result = [e for e in self.containers if e.getName() == filterName]
@ -97,14 +105,15 @@ class CryptoBoxProps:
"assign a name to a uuid in the ContainerNameDatabase" "assign a name to a uuid in the ContainerNameDatabase"
used_uuid = self.getUUIDForName(name) used_uuid = self.getUUIDForName(name)
"first remove potential conflicting uuid/name combination" "first remove potential conflicting uuid/name combination"
if used_uuid: del self.config["ContainerNameDatabase"][used_uuid] if used_uuid: del self.nameDB[used_uuid]
self.config["ContainerNameDatabase"][uuid] = name self.nameDB[uuid] = name
self.nameDB.write()
def getNameForUUID(self, uuid): def getNameForUUID(self, uuid):
"get the name belonging to a specified key (usually the UUID of a fs)" "get the name belonging to a specified key (usually the UUID of a fs)"
try: try:
return self.config["ContainerNameDatabase"][uuid] return self.nameDB[uuid]
except KeyError: except KeyError:
return None return None
@ -112,12 +121,26 @@ class CryptoBoxProps:
def getUUIDForName(self, name): def getUUIDForName(self, name):
""" get the key belonging to a value in the ContainerNameDatabase """ get the key belonging to a value in the ContainerNameDatabase
this is the reverse action of 'getNameForUUID' """ this is the reverse action of 'getNameForUUID' """
for key in self.config["ContainerNameDatabase"].keys(): for key in self.nameDB.keys():
if self.config["ContainerNameDatabase"][key] == name: return key if self.nameDB[key] == name: return key
"the uuid was not found" "the uuid was not found"
return None return None
def dropPrivileges(self):
"change the effective uid to 'User' specified in section 'System'"
if os.getuid() != os.geteuid():
raise "PrivilegeManager", "we already dropped privileges"
os.seteuid(self.__cboxUID)
def risePrivileges(self):
"regain superuser privileges temporarily - call dropPrivileges afterwards!"
if os.getuid() == os.geteuid():
raise "PrivilegeManager", "we already have superuser privileges"
os.seteuid(os.getuid())
""" ************ internal stuff starts here *********** """ """ ************ internal stuff starts here *********** """
def __getAvailablePartitions(self): def __getAvailablePartitions(self):
@ -151,9 +174,7 @@ class CryptoBoxProps:
fpart.close() fpart.close()
return [self.__getAbsoluteDeviceName(e) for e in ret_list] return [self.__getAbsoluteDeviceName(e) for e in ret_list]
except IOError: except IOError:
self.debugMessage( self.debugMessage("Could not read /proc/partitions", "warn")
"Could not read /proc/partitions",
CryptoBoxLogger.DebugLevels["warn"])
return [] return []
@ -217,12 +238,3 @@ class CryptoBoxProps:
return [] 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

View file

@ -5,10 +5,11 @@ import re
"""exceptions: """exceptions:
VolumeIsActive VolumeIsActive
InvalidName
NameActivelyUsed NameActivelyUsed
InvalidName
InvalidPassword InvalidPassword
InvalidType InvalidType
CreateError
MountError MountError
ChangePasswordError ChangePasswordError
""" """
@ -39,10 +40,12 @@ class CryptoBoxContainer:
__dmDir = "/dev/mapper" __dmDir = "/dev/mapper"
def __init__(self, device, cbox, logger): def __init__(self, device, cbox):
self.device = device self.device = device
self.logger = logger
self.cbox = cbox self.cbox = cbox
self.debugMessage = self.cbox.debugMessage
self.__dropPrivileges = self.cbox.dropPrivileges
self.__risePrivileges = self.cbox.risePrivileges
self.__resetObject() self.__resetObject()
@ -55,7 +58,7 @@ class CryptoBoxContainer:
if new_name == self.name: return if new_name == self.name: return
if self.isMounted(): if self.isMounted():
raise "VolumeIsActive", "the container must be inactive during renaming" raise "VolumeIsActive", "the container must be inactive during renaming"
if not re.search(r'^[a-zA-Z0-9_\.\- ]$', new_name): if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name):
raise "InvalidName", "the supplied new name contains illegal characters" raise "InvalidName", "the supplied new name contains illegal characters"
"check for active partitions with the same name" "check for active partitions with the same name"
prev_name_owner = self.cbox.getContainerList(filterName=new_name) prev_name_owner = self.cbox.getContainerList(filterName=new_name)
@ -107,12 +110,13 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
"remove any potential open luks mapping" "remove any potential open luks mapping"
self.__umountLuks() self.__umountLuks()
"create the luks header" "create the luks header"
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -124,12 +128,11 @@ class CryptoBoxContainer:
"luksAddKey", "luksAddKey",
self.device]) self.device])
proc.stdin.write("%s\n%s" % (oldpw, newpw)) proc.stdin.write("%s\n%s" % (oldpw, newpw))
self.__dropPrivileges()
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), ) errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["error"], errorMsg)
CryptoBoxLogger.DebugLevels["error"],
errorMsg)
raise "ChangePasswordError", errorMsg raise "ChangePasswordError", errorMsg
keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups() keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups()
if keys_found: if keys_found:
@ -137,6 +140,7 @@ class CryptoBoxContainer:
else: else:
raise "ChangePasswordError", "could not get the old key slot" raise "ChangePasswordError", "could not get the old key slot"
"remove the old key" "remove the old key"
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -149,11 +153,10 @@ class CryptoBoxContainer:
self.device, self.device,
"%d" % (keyslot, )]) "%d" % (keyslot, )])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["error"], errorMsg)
CryptoBoxLogger.DebugLevels["error"],
errorMsg)
raise "ChangePasswordError", errorMsg raise "ChangePasswordError", errorMsg
@ -179,7 +182,7 @@ class CryptoBoxContainer:
def_name = self.cbox.getNameForUUID(self.uuid) def_name = self.cbox.getNameForUUID(self.uuid)
if def_name: return def_name if def_name: return def_name
"there is no name defined for this uuid - we will propose a good one" "there is no name defined for this uuid - we will propose a good one"
prefix = self.cbox.getConfigValue("DefaultNamePrefix") prefix = self.cbox.cbxPrefs["Main"]["DefaultVolumePrefix"]
unused_found = False unused_found = False
counter = 1 counter = 1
while not unused_found: while not unused_found:
@ -198,9 +201,10 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell=False, shell=False,
stdin=None, stdin=None,
@ -213,12 +217,13 @@ class CryptoBoxContainer:
"-w", os.devnull, "-w", os.devnull,
self.device]) self.device])
proc.wait() proc.wait()
result = proc.stdout.read().strip()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), )) "retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
return None return None
result = proc.stdout.read().strip()
devnull.close() devnull.close()
if result: return result if result: return result
return self.device.replace(os.path.sep, "_") return self.device.replace(os.path.sep, "_")
@ -241,9 +246,10 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell=False, shell=False,
stdin=None, stdin=None,
@ -256,12 +262,13 @@ class CryptoBoxContainer:
"-w", os.devnull, "-w", os.devnull,
self.device]) self.device])
proc.wait() proc.wait()
output = proc.stdout.read().strip()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), )) "retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
return None return None
output = proc.stdout.read().strip()
devnull.close() devnull.close()
return output return output
@ -272,9 +279,10 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -286,13 +294,14 @@ class CryptoBoxContainer:
"isLuks", "isLuks",
self.device]) self.device])
proc.wait() proc.wait()
self.__dropPrivileges()
devnull.close() devnull.close()
return proc.returncode == 0 return proc.returncode == 0
def __getMountPoint(self): def __getMountPoint(self):
"return the name of the mountpoint of this volume" "return the name of the mountpoint of this volume"
return os.path.join(self.cbox.getConfigValue("MountParent"), self.name) return os.path.join(self.cbox.cbxPrefs["System"]["MountParentDir"], self.name)
def __mountLuks(self, password): def __mountLuks(self, password):
@ -304,9 +313,17 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
self.__cleanMountDirs()
if not os.path.exists(self.__getMountPoint()):
os.mkdir(self.__getMountPoint())
if not os.path.exists(self.__getMountPoint()):
errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), )
self.debugMessage("error", errorMsg)
raise "MountError", errorMsg
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -320,27 +337,26 @@ class CryptoBoxContainer:
self.name]) self.name])
proc.stdin.write(password) proc.stdin.write(password)
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), ) errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["warn"], errorMsg)
CryptoBoxLogger.DebugLevels["warn"],
errorMsg)
raise "MountError", errorMsg raise "MountError", errorMsg
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
stdout = devnull, stdout = devnull,
stderr = devnull, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["mount"], self.Progs["mount"],
os.path.join(self.__dmDir, self.name), os.path.join(self.__dmDir, self.name),
self.__getMountPoint()]) self.__getMountPoint()])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["warn"], errorMsg)
CryptoBoxLogger.DebugLevels["warn"],
errorMsg)
raise "MountError", errorMsg raise "MountError", errorMsg
devnull.close() devnull.close()
@ -351,24 +367,25 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
if self.isMounted(): if self.isMounted():
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
stdout = devnull, stdout = devnull,
stderr = devnull, stderr = subprocess.PIPE,
args = [self.Progs["umount"], "-l", self.__getMountPoint()]) args = [self.Progs["umount"], "-l", self.__getMountPoint()])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["warn"], errorMsg)
CryptoBoxLogger.DebugLevels["warn"],
errorMsg)
raise "MountError", errorMsg raise "MountError", errorMsg
if os.path.exists(os.path.join(self.__dmDir, self.name)): if os.path.exists(os.path.join(self.__dmDir, self.name)):
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -380,27 +397,32 @@ class CryptoBoxContainer:
"luksClose", "luksClose",
self.name]) self.name])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["warn"], errorMsg)
CryptoBoxLogger.DebugLevels["warn"],
errorMsg)
raise "MountError", errorMsg raise "MountError", errorMsg
devnull.close() devnull.close()
def __mountPlain(self): def __mountPlain(self):
"mount a plaintext partition" "mount a plaintext partition"
if self.isMounted(): raise "VolumeIsActive", "this container is already active"
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
self.__cleanMountDirs() self.__cleanMountDirs()
if not os.path.exists(self.__getMountPoint()): if not os.path.exists(self.__getMountPoint()):
os.mkdir(self.__getMountPoint()) os.mkdir(self.__getMountPoint())
if not os.path.exists(self.__getMountPoint()):
errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), )
self.debugMessage("error", errorMsg)
raise "MountError", errorMsg
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -411,11 +433,10 @@ class CryptoBoxContainer:
self.device, self.device,
self.__getMountPoint()]) self.__getMountPoint()])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["warn"], errorMsg)
CryptoBoxLogger.DebugLevels["warn"],
errorMsg)
raise "MountError", errorMsg raise "MountError", errorMsg
devnull.close() devnull.close()
@ -426,9 +447,11 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
if self.isMounted():
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -439,11 +462,10 @@ class CryptoBoxContainer:
"-l", "-l",
self.__getMountPoint()]) self.__getMountPoint()])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["warn"], errorMsg)
CryptoBoxLogger.DebugLevels["warn"],
errorMsg)
raise "MountError", errorMsg raise "MountError", errorMsg
devnull.close() devnull.close()
@ -457,9 +479,10 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -469,11 +492,10 @@ class CryptoBoxContainer:
self.Progs["mkfs-data"], self.Progs["mkfs-data"],
self.device]) self.device])
proc.wait() proc.wait()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["error"], errorMsg)
CryptoBoxLogger.DebugLevels["error"],
errorMsg)
raise "CreateError", errorMsg raise "CreateError", errorMsg
devnull.close() devnull.close()
@ -489,12 +511,13 @@ class CryptoBoxContainer:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.logger.printMessage( self.debugMessage(
CryptoBoxLogger.DebugLevels["warn"], CryptoBoxLogger.DebugLevels["warn"],
"Could not open %s" % (os.devnull, )) "Could not open %s" % (os.devnull, ))
"remove any potential open luks mapping" "remove any potential open luks mapping"
self.__umountLuks() self.__umountLuks()
"create the luks header" "create the luks header"
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -503,19 +526,19 @@ class CryptoBoxContainer:
args = [ args = [
self.Progs["cryptsetup"], self.Progs["cryptsetup"],
"--batch-mode", "--batch-mode",
"--cipher", self.cbox.getConfigValue("DefaultCipher"), "--cipher", self.cbox.cbxPrefs["System"]["DefaultCipher"],
"--iter-time", "2000", "--iter-time", "2000",
"luksFormat", "luksFormat",
self.device]) self.device])
proc.stdin.write(password) proc.stdin.write(password)
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not create the luks header: %s" % (errout.strip(), ) errorMsg = "Could not create the luks header: %s" % (errout.strip(), )
self.logger.printMessage( self.debugMessage("error", errorMsg)
CryptoBoxLogger.DebugLevels["error"],
errorMsg)
raise "CreateError", errorMsg raise "CreateError", errorMsg
"open the luks container for mkfs" "open the luks container for mkfs"
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -529,29 +552,28 @@ class CryptoBoxContainer:
self.name]) self.name])
proc.stdin.write(password) proc.stdin.write(password)
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
self.__dropPrivileges()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), ) errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["error"], errorMsg)
CryptoBoxLogger.DebugLevels["error"],
errorMsg)
raise "CreateError", errorMsg raise "CreateError", errorMsg
"make the filesystem" "make the filesystem"
self.__risePrivileges()
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
stdout = devnull, stdout = devnull,
stderr = devnull, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["mkfs-data"], self.Progs["mkfs-data"],
os.path.join(self.__dmDir, self.name)]) os.path.join(self.__dmDir, self.name)])
proc.wait() proc.wait()
self.__dropPrivileges()
"remove the mapping - for every exit status" "remove the mapping - for every exit status"
self.__umountLuks() self.__umountLuks()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.logger.printMessage( self.debugMessage(CryptoBoxLogger.DebugLevels["error"], errorMsg)
CryptoBoxLogger.DebugLevels["error"],
errorMsg)
"remove the luks mapping" "remove the luks mapping"
raise "CreateError", errorMsg raise "CreateError", errorMsg
devnull.close() devnull.close()
@ -560,9 +582,9 @@ class CryptoBoxContainer:
def __cleanMountDirs(self): def __cleanMountDirs(self):
""" remove all unnecessary subdirs of the mount parent directory """ remove all unnecessary subdirs of the mount parent directory
this should be called for every (u)mount """ this should be called for every (u)mount """
subdirs = os.listdir(self.cbox.getConfigValue("MountParent")) subdirs = os.listdir(self.cbox.cbxPrefs["System"]["MountParentDir"])
for dir in subdirs: for dir in subdirs:
abs_dir = os.path.join(self.cbox.getConfigValue("MountParent"), dir) abs_dir = os.path.join(self.cbox.cbxPrefs["System"]["MountParentDir"], dir)
if (not os.path.islink(abs_dir)) and os.path.isdir(abs_dir) and (not os.path.ismount(abs_dir)): os.rmdir(abs_dir) if (not os.path.islink(abs_dir)) and os.path.isdir(abs_dir) and (not os.path.ismount(abs_dir)): os.rmdir(abs_dir)

View file

@ -2,6 +2,7 @@
manages logging events of the CryptoBox manages logging events of the CryptoBox
''' '''
import sys import sys
import os
class CryptoBoxLogger: class CryptoBoxLogger:
''' '''
@ -11,9 +12,17 @@ class CryptoBoxLogger:
DebugLevels = {"debug":0, "info":3, "warn":6, "error":9} DebugLevels = {"debug":0, "info":3, "warn":6, "error":9}
DebugFacilities = {"file":0} DebugFacilities = {"file":0}
def __init__(self, level, facility, name=None): def __init__(self, level, facility, name=None, user=None):
self.debug_level = int(level) try:
if int(facility) == self.DebugFacilities["file"]: facility = int(facility)
except ValueError:
facility = self.DebugFacilities[facility]
try:
level = int(level)
except ValueError:
level = self.DebugLevels[level]
self.debug_level = level
if facility == self.DebugFacilities["file"]:
self.logFunc = self.message2file self.logFunc = self.message2file
if name is not None: if name is not None:
self.logFile = name self.logFile = name
@ -22,6 +31,12 @@ class CryptoBoxLogger:
try: try:
fsock = open(self.logFile, "a") fsock = open(self.logFile, "a")
fsock.close() fsock.close()
# TODO: check before, if this file was owned
# by someone else than the cryptobox user - in this case,
# we may not chown it - very baaaad!
"change ownership of log file"
if user != None:
os.chown(self.logFile, user, os.getegid())
return return
except IOError: except IOError:
sys.stderr.write("Unable to open logfile (%s) for writing.\n" % (self.logFile, )) sys.stderr.write("Unable to open logfile (%s) for writing.\n" % (self.logFile, ))
@ -32,8 +47,13 @@ class CryptoBoxLogger:
sys.exit(1) sys.exit(1)
def printMessage(self, text, msg_level=None): def printMessage(self, msg_level, text):
if msg_level is None: msg_level = self.DebugLevels["debug"] if msg_level is None: msg_level = self.DebugLevels["debug"]
"convert debuglevel from string to int, if necessary"
try:
msg_level = int(msg_level)
except ValueError:
msg_level = self.DebugLevels[msg_level]
if msg_level >= self.debug_level: if msg_level >= self.debug_level:
self.logFunc("[CryptoBox] - %s\n" % (text, )) self.logFunc("[CryptoBox] - %s\n" % (text, ))

View file

@ -1,9 +0,0 @@
[Main]
allowed_devices = /dev/loop
defaultcipher = aes-cbc-essiv:sha256
defaultnameprefix = "Data "
mountparent = /var/cache/cryptobox/mnt
debugfacility = 0
debuglevel = 0
logfile = cbox.log

View file

@ -0,0 +1,51 @@
[Main]
# comma separated list of possible prefixes for accesible devices
# beware: .e.g "/dev/hd" grants access to _all_ harddisks
AllowedDevices = /dev/loop
# the default prefix of not yet named containers
DefaultVolumePrefix = "Data "
# where should we put the local configuration and the mountpoints?
# this directory must be accessible by the cryptobox user (see below)
#DataDir = /var/cache/cryptobox
DataDir = .
# the name-database file - inside of DataDir
NameDatabase = cryptobox_names.db
[System]
# most actions of the cryptobox are not executed as root - choose a limited
# user here - for now only numeric ids are allowed
User = 1000
# where should we mount volumes?
# this directory must be writeable by the cryptobox user (see above)
MountParentDir = /var/cache/cryptobox/mnt
# which cipher should cryptsetup-luks use?
DefaultCipher = aes-cbc-essiv:sha256
[Log]
# possible values are "debug", "info", "warn" and "error" or numbers from
# 0 (debug) to 9 (error)
Level = debug
# where to write the log messages to?
# possible values are: file
# syslog support will be added later
Facility = file
# depending on the choosen facility (see above) you may select a
# destination. Possible values for the different facilities are:
# file: $FILENAME
# syslog: $LOG_FACILITY
# The log file will get created as root and then handed over to the
# cryptobox user 8see above)
#Destination = /var/log/cryptobox.log
Destination = ./cryptobox.log

View file

@ -0,0 +1,93 @@
"""
BEWARE: this script may overwrite the data of one of your loop devices. You
should restrict the AllowedDevices directive in cryptobox.conf to exclude
your precious black devices from being used by this script.
the following script runs a number of tests for different parts
"""
from CryptoBox import CryptoBoxProps
from CryptoBoxContainer import CryptoBoxContainer
import sys
def main():
cb = CryptoBoxProps()
print "Confguration:"
print "\tConfig file:\t\t%s" % (cb.cbxPrefs.filename, )
print "\tAllowed devices:\t%s" % (cb.cbxPrefs["Main"]["AllowedDevices"], )
"""for e in cb.getContainerList(filterType=CryptoBoxContainer.Types["luks"]):"""
for e in cb.getContainerList():
print "\t\t%d\t\t%s - %s - %d" % (cb.getContainerList().index(e), e.getDevice(), e.getName(), e.getType())
if not cb.getContainerList():
print "no loop devices found for testing"
sys.exit(1)
if len(cb.getContainerList()) > 1:
print "I found more than one available loop device - I will stop now to avoid risking data loss."
print "Please change the 'AllowedDevices' setting in 'cryptobox.conf' to reduce the number of allowed devices to only one."
sys.exit(1)
testElement = cb.getContainerList()[0]
print "\nRunning some tests now ..."
if not luke_tests(testElement):
print "some previous tests failed - we should stop now"
sys.exit(1)
plain_tests(testElement)
" ***************** some functions ******************** "
def luke_tests(e):
e.create(e.Types["luks"], "alt")
print "\tluks create:\tok"
e.changePassword("alt","neu")
print "\tluks changepw:\tok"
e.setName("lalla")
print "\tluks setName:\tok"
try:
e.mount("neu")
except "MountError":
pass
if e.isMounted(): print "\tluks mount:\tok"
else: print "\tluks mount:\tfailed"
try:
e.umount()
except "MountError":
pass
if e.isMounted(): print "\tluks umount:\tfailed"
else: print "\tluks umount:\tok"
if e.isMounted(): return False
else: return True
def plain_tests(e):
e.create(e.Types["plain"])
print "\tplain create:\tok"
e.setName("plain-lili")
print "\tplain setName:\tok"
try:
e.mount()
except "MountError":
pass
if e.isMounted(): print "\tplain mount:\tok"
else: print "\tplain mount:\tfailed"
try:
e.umount()
except "MountError":
pass
if e.isMounted(): print "\tplain umount:\tfailed"
else: print "\tplain umount:\tok"
main()