class CryptoBoxContainer finished
This commit is contained in:
parent
c6ef2a3eb4
commit
7443b4684e
5 changed files with 735 additions and 75 deletions
|
@ -1,17 +1,19 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
'''
|
'''
|
||||||
This is a secure fileserver with encrypted filesystem and webinterface.
|
This is a secure fileserver with encrypted filesystem and webinterface.
|
||||||
|
|
||||||
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.
|
||||||
:)
|
:)
|
||||||
'''
|
'''
|
||||||
import re
|
|
||||||
import CryptoBoxLogger
|
import CryptoBoxLogger
|
||||||
import CryptoBoxContainer
|
import CryptoBoxContainer
|
||||||
import CryptoBoxPreferences
|
import CryptoBoxPreferences
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
CONFIG_FILE="cbx.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
|
||||||
|
@ -20,43 +22,116 @@ class CryptoBoxProps:
|
||||||
be used further.
|
be used further.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
'''read config and fill class variables'''
|
'''read config and fill class variables'''
|
||||||
print CONFIG_FILE
|
print CONFIG_FILE
|
||||||
self.cbxPrefs = CryptoBoxPreferences.Preferences(CONFIG_FILE)
|
self.cbxPrefs = CryptoBoxPreferences.Preferences(CONFIG_FILE)
|
||||||
self.debug = CryptoBoxLogger.CryptoBoxLogger(
|
self.debug = CryptoBoxLogger.CryptoBoxLogger(
|
||||||
self.cbxPrefs["debuglevel"],
|
self.cbxPrefs["debuglevel"],
|
||||||
self.cbxPrefs["debugfacility"],
|
self.cbxPrefs["debugfacility"],
|
||||||
self.cbxPrefs["logfile"] )
|
self.cbxPrefs["logfile"] )
|
||||||
self.alloweddevices = self.__csv2list(self.cbxPrefs["allowed_devices"])
|
"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 __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
|
|
||||||
|
|
||||||
def __deviceIsReallyAllowed(self, device):
|
def debugMessage(self, level, text):
|
||||||
'''return "true" if the given device is white-listed for being used as cryptobox container'''
|
"print a debug message to the previously choosen debugging facility"
|
||||||
for a in self.alloweddevices:
|
self.debug.printMessage(level,text)
|
||||||
if re.search('^' + a, device):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __allAvailablePartitions(self):
|
|
||||||
'''retrieve a list of all available containers
|
def isDeviceAllowed(self, devicename):
|
||||||
|
"check if a device is white-listed for being used as cryptobox containers"
|
||||||
TODO: if the code is not like a poem, write prosadocumentation ;)
|
"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 = []
|
ret_list = []
|
||||||
try:
|
try:
|
||||||
|
"the following reads all lines of /proc/partitions and adds the mentioned devices"
|
||||||
fpart = open("/proc/partitions", "r")
|
fpart = open("/proc/partitions", "r")
|
||||||
try:
|
try:
|
||||||
line = fpart.readline()
|
line = fpart.readline()
|
||||||
while line:
|
while line:
|
||||||
p_details = line.split()
|
p_details = line.split()
|
||||||
if (len(p_details) == 4):
|
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
|
(p_major, p_minor, p_size, p_device) = p_details
|
||||||
if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor):
|
if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor):
|
||||||
p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
|
p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
|
||||||
|
@ -74,44 +149,80 @@ class CryptoBoxProps:
|
||||||
line = fpart.readline()
|
line = fpart.readline()
|
||||||
finally:
|
finally:
|
||||||
fpart.close()
|
fpart.close()
|
||||||
return ["/dev/" + 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",
|
"Could not read /proc/partitions",
|
||||||
CryptoBoxLogger.DebugLevels["warn"])
|
CryptoBoxLogger.DebugLevels["warn"])
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def debugMessage(self, level, text):
|
|
||||||
'''print a debug message to the previously choosen debugging facility'''
|
|
||||||
self.debug.printMessage(level,text)
|
|
||||||
|
|
||||||
def getContainerList(self):
|
def __getAbsoluteDeviceName(self, shortname):
|
||||||
'''return a list of all actual available containers of this cryptobox'''
|
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
|
||||||
self.containers = []
|
this does also work for device mapper devices
|
||||||
for device in self.__allAvailablePartitions():
|
if the result is non-unique, one arbitrary value is returned"""
|
||||||
if self.__deviceIsReallyAllowed(device):
|
if re.search('^/', shortname): return shortname
|
||||||
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device))
|
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:
|
try:
|
||||||
return self.containers[:]
|
f_blockdev_info = open(blockdev_info_file, "r")
|
||||||
except AttributeError:
|
blockdev_info = f_blockdev_info.read()
|
||||||
return None
|
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 getConfigValue(self, key):
|
|
||||||
'''return a tuple of key+value from the configfile'''
|
|
||||||
return (key, self.cbxPrefs[key])
|
|
||||||
|
|
||||||
def setConfigValue(self, key, value):
|
def __findMajorMinorDeviceName(self, dir, major, minor):
|
||||||
'''save the strings key+value in configfile'''
|
"returns the names of devices with the specified major and minor number"
|
||||||
self.cbxPrefs[key]=value
|
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
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
'''
|
|
||||||
Start the Cryptobox with: `python CryptoBox.py`
|
|
||||||
'''
|
|
||||||
cbprops = CryptoBoxProps()
|
|
||||||
#print "Allowed_Devices: %s" % (cb.getConfigValue("allowed_devices"), )
|
|
||||||
#print "non-existing: %s" % (cb.getConfigValue("alowed_devices"), )
|
|
||||||
print [e.device for e in cbprops.getContainerList()]
|
|
||||||
print "Config %s" % str(cbprops.getConfigValue("logfile"))
|
|
||||||
#cbprops.setConfigValue("foo","bar")
|
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,568 @@
|
||||||
'''
|
from CryptoBoxLogger import CryptoBoxLogger
|
||||||
TODO
|
import subprocess
|
||||||
'''
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
"""exceptions:
|
||||||
|
VolumeIsActive
|
||||||
|
InvalidName
|
||||||
|
NameActivelyUsed
|
||||||
|
InvalidPassword
|
||||||
|
InvalidType
|
||||||
|
MountError
|
||||||
|
ChangePasswordError
|
||||||
|
"""
|
||||||
|
|
||||||
class CryptoBoxContainer:
|
class CryptoBoxContainer:
|
||||||
'''
|
|
||||||
TODO
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, device):
|
Progs = {
|
||||||
|
"cryptsetup":"/sbin/cryptsetup",
|
||||||
|
"mkfs-data":"/sbin/mkfs.ext3",
|
||||||
|
"mkfs-config":"/sbin/mkfs.ext2",
|
||||||
|
"blkid":"/sbin/blkid",
|
||||||
|
"mount":"/bin/mount",
|
||||||
|
"umount":"/bin/umount"}
|
||||||
|
|
||||||
|
|
||||||
|
Types = {
|
||||||
|
"unused":0,
|
||||||
|
"plain":1,
|
||||||
|
"luks":2,
|
||||||
|
"swap":3}
|
||||||
|
|
||||||
|
|
||||||
|
__fsTypes = {
|
||||||
|
"plain":["ext3", "ext2", "vfat", "reiser"],
|
||||||
|
"swap":["swap"]}
|
||||||
|
"TODO: mehr Dateisystemtypen? / 'reiser' pruefen"
|
||||||
|
|
||||||
|
__dmDir = "/dev/mapper"
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, device, cbox, logger):
|
||||||
self.device = device
|
self.device = device
|
||||||
|
self.logger = logger
|
||||||
|
self.cbox = cbox
|
||||||
|
self.__resetObject()
|
||||||
|
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def setName(self, new_name):
|
||||||
|
"TODO: den Test-Code pruefen"
|
||||||
|
if new_name == self.name: return
|
||||||
|
if self.isMounted():
|
||||||
|
raise "VolumeIsActive", "the container must be inactive during renaming"
|
||||||
|
if not re.search(r'^[a-zA-Z0-9_\.\- ]$', new_name):
|
||||||
|
raise "InvalidName", "the supplied new name contains illegal characters"
|
||||||
|
"check for active partitions with the same name"
|
||||||
|
prev_name_owner = self.cbox.getContainerList(filterName=new_name)
|
||||||
|
if prev_name_owner:
|
||||||
|
for a in prev_name_owner:
|
||||||
|
if a.isMounted():
|
||||||
|
raise "NameActivelyUsed", "the supplied new name is already in use for an active partition"
|
||||||
|
self.cbox.setNameForUUID(self.uuid, new_name)
|
||||||
|
self.name = new_name
|
||||||
|
|
||||||
|
|
||||||
|
def getDevice(self):
|
||||||
|
return self.device
|
||||||
|
|
||||||
|
|
||||||
|
def getType(self):
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
|
||||||
|
def isMounted(self):
|
||||||
|
return os.path.ismount(self.__getMountPoint())
|
||||||
|
|
||||||
|
|
||||||
|
def create(self, type, password=None):
|
||||||
|
if type == self.Types["luks"]:
|
||||||
|
self.__createLuks(password)
|
||||||
|
self.__resetObject()
|
||||||
|
return
|
||||||
|
if type == self.Types["plain"]:
|
||||||
|
self.__createPlain()
|
||||||
|
self.__resetObject()
|
||||||
|
return
|
||||||
|
raise "InvalidType", "invalid container type (%d) supplied" % (type, )
|
||||||
|
|
||||||
|
|
||||||
|
def changePassword(self, oldpw, newpw):
|
||||||
|
if self.type != self.Types["luks"]:
|
||||||
|
raise "InvalidType", \
|
||||||
|
"changing of password is possible only for luks containers"
|
||||||
|
if not oldpw:
|
||||||
|
raise "InvalidPassword", "no old password supplied for password change"
|
||||||
|
if not newpw:
|
||||||
|
raise "InvalidPassword", "no new password supplied for password change"
|
||||||
|
"return if new and old passwords are the same"
|
||||||
|
if oldpw == newpw: return
|
||||||
|
if self.isMounted():
|
||||||
|
raise "VolumeIsActive", "this container is currently active"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
"remove any potential open luks mapping"
|
||||||
|
self.__umountLuks()
|
||||||
|
"create the luks header"
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = subprocess.PIPE,
|
||||||
|
stdout = subprocess.PIPE,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"--batch-mode",
|
||||||
|
"luksAddKey",
|
||||||
|
self.device])
|
||||||
|
proc.stdin.write("%s\n%s" % (oldpw, newpw))
|
||||||
|
(output, errout) = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["error"],
|
||||||
|
errorMsg)
|
||||||
|
raise "ChangePasswordError", errorMsg
|
||||||
|
keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups()
|
||||||
|
if keys_found:
|
||||||
|
keyslot = int(keys_found[0])
|
||||||
|
else:
|
||||||
|
raise "ChangePasswordError", "could not get the old key slot"
|
||||||
|
"remove the old key"
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"--batch-mode",
|
||||||
|
"luksDelKey",
|
||||||
|
self.device,
|
||||||
|
"%d" % (keyslot, )])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["error"],
|
||||||
|
errorMsg)
|
||||||
|
raise "ChangePasswordError", errorMsg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
" ****************** internal stuff ********************* "
|
||||||
|
|
||||||
|
def __resetObject(self):
|
||||||
|
""" recheck the information about this container
|
||||||
|
this is especially useful after changing the type via 'create' """
|
||||||
|
self.uuid = self.__getUUID()
|
||||||
|
self.type = self.__getTypeOfPartition()
|
||||||
|
self.name = self.__getNameOfContainer()
|
||||||
|
if self.type == self.Types["luks"]:
|
||||||
|
self.mount = self.__mountLuks
|
||||||
|
self.umount = self.__umountLuks
|
||||||
|
if self.type == self.Types["plain"]:
|
||||||
|
self.mount = self.__mountPlain
|
||||||
|
self.umount = self.__umountPlain
|
||||||
|
|
||||||
|
|
||||||
|
def __getNameOfContainer(self):
|
||||||
|
"retrieve the name of the container by querying the database"
|
||||||
|
def_name = self.cbox.getNameForUUID(self.uuid)
|
||||||
|
if def_name: return def_name
|
||||||
|
"there is no name defined for this uuid - we will propose a good one"
|
||||||
|
prefix = self.cbox.getConfigValue("DefaultNamePrefix")
|
||||||
|
unused_found = False
|
||||||
|
counter = 1
|
||||||
|
while not unused_found:
|
||||||
|
guess = prefix + str(counter)
|
||||||
|
if self.cbox.getUUIDForName(guess):
|
||||||
|
counter += 1
|
||||||
|
else:
|
||||||
|
unused_found = True
|
||||||
|
self.cbox.setNameForUUID(self.uuid, guess)
|
||||||
|
return guess
|
||||||
|
|
||||||
|
|
||||||
|
def __getUUID(self):
|
||||||
|
"""return UUID for luks partitions, ext2/3 and vfat filesystems"""
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell=False,
|
||||||
|
stdin=None,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=devnull,
|
||||||
|
args=[self.Progs["blkid"],
|
||||||
|
"-s", "UUID",
|
||||||
|
"-o", "value",
|
||||||
|
"-c", os.devnull,
|
||||||
|
"-w", os.devnull,
|
||||||
|
self.device])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
|
||||||
|
return None
|
||||||
|
result = proc.stdout.read().strip()
|
||||||
|
devnull.close()
|
||||||
|
if result: return result
|
||||||
|
return self.device.replace(os.path.sep, "_")
|
||||||
|
|
||||||
|
|
||||||
|
def __getTypeOfPartition(self):
|
||||||
|
"retrieve the type of the given partition (see CryptoBoxContainer.Types)"
|
||||||
|
if self.__isLuksPartition(): return self.Types["luks"]
|
||||||
|
typeOfPartition = self.__getTypeIdOfPartition()
|
||||||
|
if typeOfPartition in self.__fsTypes["plain"]:
|
||||||
|
return self.Types["plain"]
|
||||||
|
if typeOfPartition in self.__fsTypes["swap"]:
|
||||||
|
return self.Types["swap"]
|
||||||
|
return self.Types["unused"]
|
||||||
|
|
||||||
|
|
||||||
|
def __getTypeIdOfPartition(self):
|
||||||
|
"returns the type of the partition (see 'man blkid')"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell=False,
|
||||||
|
stdin=None,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
args=[self.Progs["blkid"],
|
||||||
|
"-s", "TYPE",
|
||||||
|
"-o", "value",
|
||||||
|
"-c", os.devnull,
|
||||||
|
"-w", os.devnull,
|
||||||
|
self.device])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
|
||||||
|
return None
|
||||||
|
output = proc.stdout.read().strip()
|
||||||
|
devnull.close()
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def __isLuksPartition(self):
|
||||||
|
"check if the given device is a luks partition"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = devnull,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"--batch-mode",
|
||||||
|
"isLuks",
|
||||||
|
self.device])
|
||||||
|
proc.wait()
|
||||||
|
devnull.close()
|
||||||
|
return proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def __getMountPoint(self):
|
||||||
|
"return the name of the mountpoint of this volume"
|
||||||
|
return os.path.join(self.cbox.getConfigValue("MountParent"), self.name)
|
||||||
|
|
||||||
|
|
||||||
|
def __mountLuks(self, password):
|
||||||
|
"mount a luks partition"
|
||||||
|
if not password:
|
||||||
|
raise "InvalidPassword", "no password supplied for luksOpen"
|
||||||
|
if self.isMounted(): raise "VolumeIsActive", "this container is already active"
|
||||||
|
self.__umountLuks()
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = subprocess.PIPE,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"luksOpen",
|
||||||
|
"--batch-mode",
|
||||||
|
self.device,
|
||||||
|
self.name])
|
||||||
|
proc.stdin.write(password)
|
||||||
|
(output, errout) = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
errorMsg)
|
||||||
|
raise "MountError", errorMsg
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = devnull,
|
||||||
|
args = [
|
||||||
|
self.Progs["mount"],
|
||||||
|
os.path.join(self.__dmDir, self.name),
|
||||||
|
self.__getMountPoint()])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
errorMsg)
|
||||||
|
raise "MountError", errorMsg
|
||||||
|
devnull.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __umountLuks(self):
|
||||||
|
"umount a luks partition"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
if self.isMounted():
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = devnull,
|
||||||
|
args = [self.Progs["umount"], "-l", self.__getMountPoint()])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
errorMsg)
|
||||||
|
raise "MountError", errorMsg
|
||||||
|
if os.path.exists(os.path.join(self.__dmDir, self.name)):
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"--batch-mode",
|
||||||
|
"luksClose",
|
||||||
|
self.name])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
errorMsg)
|
||||||
|
raise "MountError", errorMsg
|
||||||
|
devnull.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __mountPlain(self):
|
||||||
|
"mount a plaintext partition"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
self.__cleanMountDirs()
|
||||||
|
if not os.path.exists(self.__getMountPoint()):
|
||||||
|
os.mkdir(self.__getMountPoint())
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["mount"],
|
||||||
|
self.device,
|
||||||
|
self.__getMountPoint()])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
errorMsg)
|
||||||
|
raise "MountError", errorMsg
|
||||||
|
devnull.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __umountPlain(self):
|
||||||
|
"umount a plaintext partition"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["umount"],
|
||||||
|
"-l",
|
||||||
|
self.__getMountPoint()])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
errorMsg)
|
||||||
|
raise "MountError", errorMsg
|
||||||
|
devnull.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __createPlain(self):
|
||||||
|
"make a plaintext partition"
|
||||||
|
if self.isMounted():
|
||||||
|
raise "VolumeIsActive", \
|
||||||
|
"deactivate the partition before filesystem initialization"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["mkfs-data"],
|
||||||
|
self.device])
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["error"],
|
||||||
|
errorMsg)
|
||||||
|
raise "CreateError", errorMsg
|
||||||
|
devnull.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __createLuks(self, password):
|
||||||
|
"make a luks partition"
|
||||||
|
if not password:
|
||||||
|
raise "InvalidPassword", "no password supplied for new luks mapping"
|
||||||
|
if self.isMounted():
|
||||||
|
raise "VolumeIsActive", \
|
||||||
|
"deactivate the partition before filesystem initialization"
|
||||||
|
devnull = None
|
||||||
|
try:
|
||||||
|
devnull = open(os.devnull, "w")
|
||||||
|
except IOError:
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["warn"],
|
||||||
|
"Could not open %s" % (os.devnull, ))
|
||||||
|
"remove any potential open luks mapping"
|
||||||
|
self.__umountLuks()
|
||||||
|
"create the luks header"
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = subprocess.PIPE,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"--batch-mode",
|
||||||
|
"--cipher", self.cbox.getConfigValue("DefaultCipher"),
|
||||||
|
"--iter-time", "2000",
|
||||||
|
"luksFormat",
|
||||||
|
self.device])
|
||||||
|
proc.stdin.write(password)
|
||||||
|
(output, errout) = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not create the luks header: %s" % (errout.strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["error"],
|
||||||
|
errorMsg)
|
||||||
|
raise "CreateError", errorMsg
|
||||||
|
"open the luks container for mkfs"
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = subprocess.PIPE,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [
|
||||||
|
self.Progs["cryptsetup"],
|
||||||
|
"--batch-mode",
|
||||||
|
"luksOpen",
|
||||||
|
self.device,
|
||||||
|
self.name])
|
||||||
|
proc.stdin.write(password)
|
||||||
|
(output, errout) = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["error"],
|
||||||
|
errorMsg)
|
||||||
|
raise "CreateError", errorMsg
|
||||||
|
"make the filesystem"
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdin = None,
|
||||||
|
stdout = devnull,
|
||||||
|
stderr = devnull,
|
||||||
|
args = [
|
||||||
|
self.Progs["mkfs-data"],
|
||||||
|
os.path.join(self.__dmDir, self.name)])
|
||||||
|
proc.wait()
|
||||||
|
"remove the mapping - for every exit status"
|
||||||
|
self.__umountLuks()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
|
||||||
|
self.logger.printMessage(
|
||||||
|
CryptoBoxLogger.DebugLevels["error"],
|
||||||
|
errorMsg)
|
||||||
|
"remove the luks mapping"
|
||||||
|
raise "CreateError", errorMsg
|
||||||
|
devnull.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __cleanMountDirs(self):
|
||||||
|
""" remove all unnecessary subdirs of the mount parent directory
|
||||||
|
this should be called for every (u)mount """
|
||||||
|
subdirs = os.listdir(self.cbox.getConfigValue("MountParent"))
|
||||||
|
for dir in subdirs:
|
||||||
|
abs_dir = os.path.join(self.cbox.getConfigValue("MountParent"), 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CryptoBox] - 6
|
||||||
|
[CryptoBox] - 6
|
||||||
|
[CryptoBox] - 6
|
||||||
|
[CryptoBox] - 6
|
|
@ -1,7 +1,9 @@
|
||||||
[Main]
|
[Main]
|
||||||
logfile = cbox.log
|
allowed_devices = /dev/loop
|
||||||
allowed_devices = /dev/sda1, /dev/hda1, /dev/hda3, asf
|
defaultcipher = aes-cbc-essiv:sha256
|
||||||
|
defaultnameprefix = "Data "
|
||||||
|
mountparent = /var/cache/cryptobox/mnt
|
||||||
debugfacility = 0
|
debugfacility = 0
|
||||||
debuglevel = 0
|
debuglevel = 0
|
||||||
foo = bar
|
logfile = cbox.log
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,5 @@ this is the future plan for the cbx python classes, you can find the actual clas
|
||||||
|
|
||||||
Classes and Methods:
|
Classes and Methods:
|
||||||
CryptoBoxContainer
|
CryptoBoxContainer
|
||||||
getName
|
|
||||||
getDevice
|
|
||||||
getUUID
|
|
||||||
isMounted
|
|
||||||
getCapacity
|
getCapacity
|
||||||
mount
|
|
||||||
umount
|
|
||||||
create(type, opt: pw...)
|
|
||||||
getType
|
|
||||||
CryptoBoxContainerPlain (CryptoBoxContainer)
|
|
||||||
CryptoBoxContainerLuks (CryptoBoxContainer)
|
|
||||||
getCipher
|
|
||||||
changePassword
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue