diff --git a/pythonrewrite/bin/CryptoBox.py b/pythonrewrite/bin/CryptoBox.py index 44d51b4..9d245a2 100755 --- a/pythonrewrite/bin/CryptoBox.py +++ b/pythonrewrite/bin/CryptoBox.py @@ -19,6 +19,7 @@ from CryptoBoxExceptions import * import re import os import CryptoBoxTools +import subprocess @@ -75,7 +76,6 @@ class CryptoBox: def __runTestRootPriv(self): """try to run 'super' with 'CryptoBoxRootActions'""" - import subprocess try: devnull = open(os.devnull, "w") except IOError: @@ -130,25 +130,7 @@ class CryptoBoxProps(CryptoBox): self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1) - def getConfigPartitions(self): - """returns a sequence of found config partitions""" - import subprocess - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - args = [ - self.prefs["Programs"]["blkid"], - "-c", os.path.devnull, - "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) - (output, error) = proc.communicate() - if output: - return [e.strip().split(":",1)[0] for e in output.splitlines()] - else: - return [] - - def isConfigPartition(self, device): - import subprocess proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, @@ -270,9 +252,9 @@ class CryptoBoxProps(CryptoBox): '''reads all files in path LangDir and returns a list of basenames from existing hdf files, that should are all available languages''' - languages = [] - for file in os.listdir(self.prefs["Locations"]["LangDir"]): - if file.endswith(".hdf"): languages.append(file.rstrip(".hdf")) + 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 @@ -280,5 +262,5 @@ class CryptoBoxProps(CryptoBox): if __name__ == "__main__": - cb = CryptoBox() + cb = CryptoBoxProps() diff --git a/pythonrewrite/bin/CryptoBoxContainer.py b/pythonrewrite/bin/CryptoBoxContainer.py index a35210c..58bf850 100755 --- a/pythonrewrite/bin/CryptoBoxContainer.py +++ b/pythonrewrite/bin/CryptoBoxContainer.py @@ -45,7 +45,6 @@ class CryptoBoxContainer: self.device = device self.cbox = cbox self.log = logging.getLogger("CryptoBox") - self.Progs = self.cbox.prefs["Programs"] self.resetObject() @@ -157,8 +156,8 @@ class CryptoBoxContainer: stdout = subprocess.PIPE, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "cryptsetup", "luksAddKey", self.device, @@ -182,7 +181,7 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["cryptsetup"], + self.cbox.prefs["Programs"]["cryptsetup"], "--batch-mode", "luksDelKey", self.device, @@ -228,7 +227,7 @@ class CryptoBoxContainer: stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - args=[self.Progs["blkid"], + args=[self.cbox.prefs["Programs"]["blkid"], "-s", "UUID", "-o", "value", "-c", os.devnull, @@ -267,7 +266,7 @@ class CryptoBoxContainer: stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - args=[self.Progs["blkid"], + args=[self.cbox.prefs["Programs"]["blkid"], "-s", "TYPE", "-o", "value", "-c", os.devnull, @@ -295,7 +294,7 @@ class CryptoBoxContainer: stdout = devnull, stderr = devnull, args = [ - self.Progs["cryptsetup"], + self.cbox.prefs["Programs"]["cryptsetup"], "--batch-mode", "isLuks", self.device]) @@ -332,8 +331,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "cryptsetup", "luksOpen", self.device, @@ -351,8 +350,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "mount", os.path.join(self.__dmDir, self.name), self.__getMountPoint()]) @@ -378,8 +377,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "umount", self.__getMountPoint()]) proc.wait() @@ -394,8 +393,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "cryptsetup", "luksClose", self.name, @@ -429,8 +428,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "mount", self.device, self.__getMountPoint()]) @@ -456,8 +455,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "umount", self.__getMountPoint()]) proc.wait() @@ -483,7 +482,7 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["mkfs-data"], + self.cbox.prefs["Programs"]["mkfs-data"], self.device]) proc.wait() if proc.returncode != 0: @@ -513,8 +512,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "cryptsetup", "luksFormat", self.device, @@ -534,8 +533,8 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["super"], - self.Progs["CryptoBoxRootActions"], + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "cryptsetup", "luksOpen", self.device, @@ -554,7 +553,7 @@ class CryptoBoxContainer: stdout = devnull, stderr = subprocess.PIPE, args = [ - self.Progs["mkfs-data"], + self.cbox.prefs["Programs"]["mkfs-data"], os.path.join(self.__dmDir, self.name)]) proc.wait() "remove the mapping - for every exit status" diff --git a/pythonrewrite/bin/CryptoBoxRootActions.py b/pythonrewrite/bin/CryptoBoxRootActions.py index e1dc92c..77fe7f6 100755 --- a/pythonrewrite/bin/CryptoBoxRootActions.py +++ b/pythonrewrite/bin/CryptoBoxRootActions.py @@ -78,7 +78,7 @@ def call_plugin(args): proc = subprocess.Popen( shell = False, args = args) - proc.communicate() + proc.wait() return proc.returncode == 0 @@ -169,7 +169,7 @@ def run_cryptsetup(args): proc = subprocess.Popen( shell = False, args = cs_args) - proc.communicate() + proc.wait() ## chown the devmapper block device to the cryptobox user if (proc.returncode == 0) and (action == "luksOpen"): os.chown(os.path.join(os.path.sep, "dev", "mapper", destination), os.getuid(), os.getgid()) @@ -214,10 +214,23 @@ def run_mount(args): proc = subprocess.Popen( shell = False, args = [allowedProgs["mount"], device, destination]) - proc.communicate() - # restore previous real uid - os.setuid(savedUID) - return proc.returncode == 0 + proc.wait() + ## return in case of an error + if proc.returncode != 0: + return False + ## chown the mounted directory - otherwise it will not be writeable for + ## the cryptobox user (at least for the configuration partition this is + ## absolutely necessary) TODO: check if this is valid for data, too + (trustUserName, trustUID, groupsOfTrustUser) = getUserInfo(savedUID) + try: + os.chown(destination, trustUID, groupsOfTrustUser[0]) + except OSError, errMsg: + sys.stderr.write("could not chown the mount destination (%s) to the specified user (%d/%d): %s\n" % (destination, trustUID, groupsOfTrustUser[0], errMsg)) + sys.stderr.write("UID: %d\n" % (os.geteuid(),)) + return False + ## BEWARE: it would be nice, if we could restore the previous uid (not euid) but + ## this would also override the euid (see 'man 2 setuid') - any ideas? + return True def run_umount(args): @@ -242,7 +255,7 @@ def run_umount(args): proc = subprocess.Popen( shell = False, args = [allowedProgs["umount"], "-l", destination]) - proc.communicate() + proc.wait() # restore previous real uid os.setuid(savedUID) return proc.returncode == 0 diff --git a/pythonrewrite/bin/CryptoBoxSettings.py b/pythonrewrite/bin/CryptoBoxSettings.py index 222cd57..d8cd681 100644 --- a/pythonrewrite/bin/CryptoBoxSettings.py +++ b/pythonrewrite/bin/CryptoBoxSettings.py @@ -5,6 +5,7 @@ except: raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.") import os import CryptoBoxExceptions +import subprocess try: import configobj ## needed for reading and writing of the config file except: @@ -65,6 +66,92 @@ class CryptoBoxSettings: return ok + def isWriteable(self): + return os.access(self.prefs["Locations"]["SettingsDir"], os.W_OK) + + + def getActivePartition(self): + settings_dir = self.prefs["Locations"]["SettingsDir"] + if not os.path.ismount(settings_dir): return None + for line in file("/proc/mounts"): + fields = line.split(" ") + mount_dir = fields[1] + try: + if os.path.samefile(mount_dir, settings_dir): return fields[0] + except OSError: + pass + ## no matching entry found + return None + + + def mountPartition(self): + if self.isWriteable(): + self.log.warn("mountConfigPartition: configuration is already writeable - mounting anyway") + if self.getActivePartition(): + self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again") + return False + confPartitions = self.getAvailablePartitions() + if not confPartitions: + self.log.error("no configuration partitions found - you have to create it first") + return False + partition = confPartitions[0] + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "mount", + partition, + self.prefs["Locations"]["SettingsDir"]]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("failed to mount the configuration partition: %s" % partition) + self.log.error("output of mount: %s" % (stderr,)) + return False + self.log.info("configuration partition mounted: %s" % partition) + return True + + + def umountPartition(self): + if not self.getActivePartition(): + self.log.warn("umountConfigPartition: no configuration partition mounted") + return False + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "umount", + self.prefs["Locations"]["SettingsDir"]]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("failed to unmount the configuration partition") + self.log.error("output of mount: %s" % (stderr,)) + return False + self.log.info("configuration partition unmounted") + return True + + + def getAvailablePartitions(self): + """returns a sequence of found config partitions""" + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ + self.prefs["Programs"]["blkid"], + "-c", os.path.devnull, + "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) + (output, error) = proc.communicate() + if output: + return [e.strip().split(":",1)[0] for e in output.splitlines()] + else: + return [] + + def __getitem__(self, key): """redirect all requests to the 'prefs' attribute""" return self.prefs[key] @@ -282,6 +369,7 @@ Language = string(min=1, default="en") cryptsetup = fileExecutable(default="/sbin/cryptsetup") mkfs-data = fileExecutable(default="/sbin/mkfs.ext3") blkid = fileExecutable(default="/sbin/blkid") +blockdev = fileExecutable(default="/sbin/blockdev") mount = fileExecutable(default="/bin/mount") umount = fileExecutable(default="/bin/umount") super = fileExecutable(default="/usr/bin/super") diff --git a/pythonrewrite/bin/WebInterfaceSites.py b/pythonrewrite/bin/WebInterfaceSites.py index d13f9c0..0e8cde1 100755 --- a/pythonrewrite/bin/WebInterfaceSites.py +++ b/pythonrewrite/bin/WebInterfaceSites.py @@ -5,6 +5,16 @@ import Plugins from CryptoBoxExceptions import * import cherrypy import types +import os + +try: + import neo_cgi, neo_util, neo_cs +except ImportError: + errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'." + self.log.error(errorMsg) + sys.stderr.write(errorMsg) + raise ImportError, errorMsg + class WebInterfacePlugins: @@ -115,6 +125,7 @@ class WebInterfaceSites: def index(self, weblang=""): self.__resetDataset() self.__setWebLang(weblang) + self.__checkEnvironment() ## do not forget the language! param_dict = {"weblang":weblang} ## render "disks" plugin by default @@ -124,6 +135,7 @@ class WebInterfaceSites: def return_plugin_action(self, plugin): def handler(self, **args): self.__resetDataset() + self.__checkEnvironment() args_orig = dict(args) try: self.__setWebLang(args["weblang"]) @@ -180,6 +192,7 @@ class WebInterfaceSites: def test(self, weblang=""): self.__resetDataset() self.__setWebLang(weblang) + self.__checkEnvironment() return "test passed" @@ -197,6 +210,36 @@ class WebInterfaceSites: ##################### input checker ########################## + + def __checkEnvironment(self): + """here we should place all interesting checks to inform the user of problems + + examples are: non-https, readonly-config, ... + """ + if not self.cbox.prefs.isWriteable(): + self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig" + # TODO: turn this on soon (add "not") - for now it is annoying + if self.__checkHTTPS(): + self.dataset["Data.EnvironmentWarning"] = "NoSSL" + + + def __checkHTTPS(self): + ## check the request scheme + if cherrypy.request.scheme == "https": return True + ## check an environment setting - this is quite common behind proxies + try: + if os.environ["HTTPS"]: return True + except KeyError: + pass + ## check http header TODO (check pound for the name) + try: + if cherrypy.request.headers["TODO"]: return True + except KeyError: + pass + ## the connection seems to be unencrypted + return False + + def __setWebLang(self, value): guess = value availLangs = self.cbox.getAvailableLanguages() @@ -229,7 +272,6 @@ class WebInterfaceSites: """guess the preferred language of the user (as sent by the browser) take the first language, that is part of 'availLangs' """ - import cherrypy try: pref_lang_header = cherrypy.request.headers["Accept-Language"] except KeyError: @@ -277,7 +319,6 @@ class WebInterfaceSites: def __getLanguageData(self, web_lang="en"): - import neo_cgi, neo_util, os default_lang = "en" conf_lang = self.prefs["WebSettings"]["Language"] hdf = neo_util.HDF() @@ -302,15 +343,6 @@ class WebInterfaceSites: def __render(self, renderInfo, plugin=None): '''renders from clearsilver templates and returns the resulting html ''' - import os, types - try: - import neo_cgi, neo_util, neo_cs - except ImportError: - errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'." - self.log.error(errorMsg) - sys.stderr.write(errorMsg) - raise ImportError, errorMsg - ## is renderInfo a string (filename of the template) or a dictionary? if type(renderInfo) == types.DictType: template = renderInfo["template"]