From 974a49ccc0542949389a599e9577c3d674235c76 Mon Sep 17 00:00:00 2001 From: lars Date: Thu, 9 Nov 2006 12:00:52 +0000 Subject: [PATCH] write nameDB immediately after change add support for external hook scripts --- bin/CryptoBox.py | 21 ++++++++++++++++ bin/CryptoBoxContainer.py | 48 ++++++++++++++++++++++++------------- bin/CryptoBoxRootActions.py | 46 ++++++++++++++++++++++++++++------- bin/CryptoBoxSettings.py | 1 + bin/cryptobox.conf | 3 +++ bin/unittests.CryptoBox.py | 1 + 6 files changed, 96 insertions(+), 24 deletions(-) diff --git a/bin/CryptoBox.py b/bin/CryptoBox.py index 1472b53..0612b23 100755 --- a/bin/CryptoBox.py +++ b/bin/CryptoBox.py @@ -252,6 +252,7 @@ class CryptoBoxProps(CryptoBox): def removeUUID(self, uuid): if uuid in self.prefs.nameDB.keys(): del self.prefs.nameDB[uuid] + self.prefs.nameDB.write() return True else: return False @@ -269,6 +270,26 @@ class CryptoBoxProps(CryptoBox): return languages + def sendEventNotification(self, event, event_infos): + """call all available scripts in the hook directory with some event information""" + hook_dir = self.prefs["Locations"]["HookDir"] + for fname in os.listdir(hook_dir): + real_fname = os.path.join(hook_dir, fname) + if os.path.isfile(real_fname) and os.access(real_fname, os.X_OK): + cmd_args = [ self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "hook", real_fname, event] + cmd_args.extend(event_infos) + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = cmd_args) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.warn("a hook script (%s) failed (exitcode=%d) to handle an event (%s): %s" % (real_fname, proc.returncode, event, stderr.strip())) + else: + self.log.info("event handler (%s) finished successfully: %s" % (real_fname, event)) if __name__ == "__main__": diff --git a/bin/CryptoBoxContainer.py b/bin/CryptoBoxContainer.py index 42dc953..9d1a52d 100755 --- a/bin/CryptoBoxContainer.py +++ b/bin/CryptoBoxContainer.py @@ -354,6 +354,7 @@ class CryptoBoxContainer: errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) self.log.error(errorMsg) raise CBMountError(errorMsg) + self.cbox.sendEventNotification("premount", self.__getEventArgs()) proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, @@ -390,6 +391,7 @@ class CryptoBoxContainer: self.log.warn(errorMsg) raise CBMountError(errorMsg) devnull.close() + self.cbox.sendEventNotification("postmount", self.__getEventArgs()) def __umountLuks(self): @@ -399,6 +401,7 @@ class CryptoBoxContainer: devnull = open(os.devnull, "w") except IOError: self.log.warn("Could not open %s" % (os.devnull, )) + self.cbox.sendEventNotification("preumount", self.__getEventArgs()) if self.isMounted(): proc = subprocess.Popen( shell = False, @@ -434,6 +437,7 @@ class CryptoBoxContainer: self.log.warn(errorMsg) raise CBUmountError(errorMsg) devnull.close() + self.cbox.sendEventNotification("postumount", self.__getEventArgs()) def __mountPlain(self): @@ -451,6 +455,7 @@ class CryptoBoxContainer: errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) self.log.error(errorMsg) raise CBMountError(errorMsg) + self.cbox.sendEventNotification("premount", self.__getEventArgs()) proc = subprocess.Popen( shell = False, stdin = None, @@ -468,32 +473,37 @@ class CryptoBoxContainer: self.log.warn(errorMsg) raise CBMountError(errorMsg) devnull.close() + self.cbox.sendEventNotification("postmount", self.__getEventArgs()) def __umountPlain(self): "umount a plaintext partition" + if not self.isMounted(): + self.cbox.log.info("trying to umount while volume (%s) is mounted" % self.getDevice()) + return devnull = None try: devnull = open(os.devnull, "w") except IOError: self.log.warn("Could not open %s" % (os.devnull, )) - if self.isMounted(): - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = devnull, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "umount", - self.__getMountPoint()]) - proc.wait() - if proc.returncode != 0: - errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.warn(errorMsg) - raise CBUmountError(errorMsg) + self.cbox.sendEventNotification("preumount", self.__getEventArgs()) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "umount", + self.__getMountPoint()]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.warn(errorMsg) + raise CBUmountError(errorMsg) devnull.close() + self.cbox.sendEventNotification("postumount", self.__getEventArgs()) def __createPlain(self): @@ -605,3 +615,9 @@ class CryptoBoxContainer: os.rmdir(abs_dir) + def __getEventArgs(self): + """return an array of arguments for hook scripts handling pre/post-mount/umount + events""" + typeText = [e for e in self.Types.keys() if self.Types[e] == self.getType()][0] + return [self.getDevice(), self.getName(), typeText, self.__getMountPoint()] + diff --git a/bin/CryptoBoxRootActions.py b/bin/CryptoBoxRootActions.py index b92ae3c..74bbdb7 100755 --- a/bin/CryptoBoxRootActions.py +++ b/bin/CryptoBoxRootActions.py @@ -30,11 +30,11 @@ allowedProgs = { DEV_TYPES = { "pipe":1, "char":2, "dir":4, "block":6, "file":8, "link":10, "socket":12} -def checkIfPluginIsSafe(plugin): - """check if the plugin and its parents are only writeable for root""" +def checkIfFileIsSafe(fname): + """check if the file and its parents are only writeable for root""" #FIXME: for now we may skip this test - but users will not like it this way :) return True - props = os.stat(plugin) + props = os.stat(fname) ## check if it is owned by non-root if props.st_uid != 0: return False ## check group-write permission if gid is not zero @@ -42,9 +42,9 @@ def checkIfPluginIsSafe(plugin): ## check if it is world-writeable if props.st_mode % 4 / 2 > 0: return False ## are we at root-level (directory-wise)? If yes, then we are ok ... - if plugin == os.path.sep: return True + if fname == os.path.sep: return True ## check if the parent directory is ok - recursively :) - return checkIfPluginIsSafe(os.path.dirname(os.path.abspath(plugin))) + return checkIfFileIsSafe(os.path.dirname(os.path.abspath(fname))) def checkIfPluginIsValid(plugin): @@ -66,12 +66,12 @@ def call_plugin(args): """check if the plugin may be called - and do it finally ...""" plugin = os.path.abspath(args[0]) del args[0] - ## check existence and excutability + ## check existence and if it is executable if not os.access(plugin, os.X_OK): raise Exception, "could not find executable plugin (%s)" % plugin ## check if the plugin (and its parents) are only writeable for root - if not checkIfPluginIsSafe(plugin): - raise Exception, "the plugin (%s) was not safe - check its (and its parents') permissions" % plugin + if not checkIfFileIsSafe(plugin): + raise Exception, "the plugin (%s) is not safe - check its (and its parents') permissions" % plugin ## check if the plugin is a python program, that is marked as a cryptobox plugin if not checkIfPluginIsValid(plugin): raise Exception, "the plugin (%s) is not a correctly marked python script" % plugin @@ -83,6 +83,24 @@ def call_plugin(args): return proc.returncode == 0 +def call_hook(args): + """check if the hook script may be called - and do it finally ...""" + hook = os.path.abspath(args[0]) + del args[0] + ## check existence and if it is executable + if not os.access(hook, os.X_OK): + raise Exception, "could not find executable hook script (%s)" % hook + ## check if the hook (and its parents) are only writeable for root + if not checkIfFileIsSafe(hook): + raise Exception, "the hook (%s) is not safe - check its (and its parents') permissions" % hook + args.insert(0,hook) + proc = subprocess.Popen( + shell = False, + args = args) + proc.wait() + return proc.returncode == 0 + + def isWriteable(device, force_dev_type=None): """check if the calling user (not root!) has write access to the device/file @@ -356,6 +374,18 @@ if __name__ == "__main__": else: sys.exit(1) + if args[0].lower() == "hook": + del args[0] + try: + isOK = call_hook(args) + except Exception, errMsg: + sys.stderr.write("Execution of hook script failed: %s\n" % errMsg) + sys.exit(100) + if isOK: + sys.exit(0) + else: + sys.exit(1) + # check parameters count if len(args) < 2: sys.stderr.write("Not enough arguments supplied (%s)!\n" % " ".join(args)) diff --git a/bin/CryptoBoxSettings.py b/bin/CryptoBoxSettings.py index 8a59e9e..5c35e20 100644 --- a/bin/CryptoBoxSettings.py +++ b/bin/CryptoBoxSettings.py @@ -363,6 +363,7 @@ TemplateDir = directoryExists(default="/usr/share/cryptobox/template") LangDir = directoryExists(default="/usr/share/cryptobox/lang") DocDir = directoryExists(default="/usr/share/doc/cryptobox/html") PluginDir = directoryExists(default="/usr/share/cryptobox/plugins") +HookDir = directoryExists(default="/etc/cryptobox/hooks") [Log] Level = option("debug", "info", "warn", "error", default="warn") diff --git a/bin/cryptobox.conf b/bin/cryptobox.conf index 02ef334..4a4f11e 100644 --- a/bin/cryptobox.conf +++ b/bin/cryptobox.conf @@ -42,6 +42,9 @@ DocDir = ../doc/html #PluginDir = /usr/share/cryptobox/plugins PluginDir = ../plugins +# path to the hook directory (e.g. containing some scripts) +#HookDir = /etc/cryptobox/hooks +HookDir = ../hook-scripts [Log] diff --git a/bin/unittests.CryptoBox.py b/bin/unittests.CryptoBox.py index baaad3c..3315d4b 100755 --- a/bin/unittests.CryptoBox.py +++ b/bin/unittests.CryptoBox.py @@ -51,6 +51,7 @@ TemplateDir = ../templates LangDir = ../lang DocDir = ../doc/html PluginDir = ../plugins +HookDir = ../hook-scripts [Log] Level = debug Destination = file