added mounting and unmounting of config partition

moved config partition handling to CryptoBoxSettings
implemented environment checks (writeable config, https (off for now))
chown mounted directory after mount to the cryptobox user
This commit is contained in:
lars 2006-11-03 14:27:19 +00:00
parent 2c350207c9
commit 0fe6d426ed
5 changed files with 180 additions and 66 deletions

View file

@ -19,6 +19,7 @@ from CryptoBoxExceptions import *
import re import re
import os import os
import CryptoBoxTools import CryptoBoxTools
import subprocess
@ -75,7 +76,6 @@ class CryptoBox:
def __runTestRootPriv(self): def __runTestRootPriv(self):
"""try to run 'super' with 'CryptoBoxRootActions'""" """try to run 'super' with 'CryptoBoxRootActions'"""
import subprocess
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
@ -130,25 +130,7 @@ class CryptoBoxProps(CryptoBox):
self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1) 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): def isConfigPartition(self, device):
import subprocess
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
@ -270,9 +252,9 @@ class CryptoBoxProps(CryptoBox):
'''reads all files in path LangDir and returns a list of '''reads all files in path LangDir and returns a list of
basenames from existing hdf files, that should are all available basenames from existing hdf files, that should are all available
languages''' languages'''
languages = [] languages = [ f.rstrip(".hdf")
for file in os.listdir(self.prefs["Locations"]["LangDir"]): for f in os.listdir(self.prefs["Locations"]["LangDir"])
if file.endswith(".hdf"): languages.append(file.rstrip(".hdf")) if f.endswith(".hdf") ]
if len(languages) < 1: if len(languages) < 1:
self.log.error("No .hdf files found! The website won't render properly.") self.log.error("No .hdf files found! The website won't render properly.")
return languages return languages
@ -280,5 +262,5 @@ class CryptoBoxProps(CryptoBox):
if __name__ == "__main__": if __name__ == "__main__":
cb = CryptoBox() cb = CryptoBoxProps()

View file

@ -45,7 +45,6 @@ class CryptoBoxContainer:
self.device = device self.device = device
self.cbox = cbox self.cbox = cbox
self.log = logging.getLogger("CryptoBox") self.log = logging.getLogger("CryptoBox")
self.Progs = self.cbox.prefs["Programs"]
self.resetObject() self.resetObject()
@ -157,8 +156,8 @@ class CryptoBoxContainer:
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"cryptsetup", "cryptsetup",
"luksAddKey", "luksAddKey",
self.device, self.device,
@ -182,7 +181,7 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["cryptsetup"], self.cbox.prefs["Programs"]["cryptsetup"],
"--batch-mode", "--batch-mode",
"luksDelKey", "luksDelKey",
self.device, self.device,
@ -228,7 +227,7 @@ class CryptoBoxContainer:
stdin=None, stdin=None,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
args=[self.Progs["blkid"], args=[self.cbox.prefs["Programs"]["blkid"],
"-s", "UUID", "-s", "UUID",
"-o", "value", "-o", "value",
"-c", os.devnull, "-c", os.devnull,
@ -267,7 +266,7 @@ class CryptoBoxContainer:
stdin=None, stdin=None,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
args=[self.Progs["blkid"], args=[self.cbox.prefs["Programs"]["blkid"],
"-s", "TYPE", "-s", "TYPE",
"-o", "value", "-o", "value",
"-c", os.devnull, "-c", os.devnull,
@ -295,7 +294,7 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = devnull, stderr = devnull,
args = [ args = [
self.Progs["cryptsetup"], self.cbox.prefs["Programs"]["cryptsetup"],
"--batch-mode", "--batch-mode",
"isLuks", "isLuks",
self.device]) self.device])
@ -332,8 +331,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"cryptsetup", "cryptsetup",
"luksOpen", "luksOpen",
self.device, self.device,
@ -351,8 +350,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"mount", "mount",
os.path.join(self.__dmDir, self.name), os.path.join(self.__dmDir, self.name),
self.__getMountPoint()]) self.__getMountPoint()])
@ -378,8 +377,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"umount", "umount",
self.__getMountPoint()]) self.__getMountPoint()])
proc.wait() proc.wait()
@ -394,8 +393,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"cryptsetup", "cryptsetup",
"luksClose", "luksClose",
self.name, self.name,
@ -429,8 +428,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"mount", "mount",
self.device, self.device,
self.__getMountPoint()]) self.__getMountPoint()])
@ -456,8 +455,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"umount", "umount",
self.__getMountPoint()]) self.__getMountPoint()])
proc.wait() proc.wait()
@ -483,7 +482,7 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["mkfs-data"], self.cbox.prefs["Programs"]["mkfs-data"],
self.device]) self.device])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
@ -513,8 +512,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"cryptsetup", "cryptsetup",
"luksFormat", "luksFormat",
self.device, self.device,
@ -534,8 +533,8 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["super"], self.cbox.prefs["Programs"]["super"],
self.Progs["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"cryptsetup", "cryptsetup",
"luksOpen", "luksOpen",
self.device, self.device,
@ -554,7 +553,7 @@ class CryptoBoxContainer:
stdout = devnull, stdout = devnull,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.Progs["mkfs-data"], self.cbox.prefs["Programs"]["mkfs-data"],
os.path.join(self.__dmDir, self.name)]) os.path.join(self.__dmDir, self.name)])
proc.wait() proc.wait()
"remove the mapping - for every exit status" "remove the mapping - for every exit status"

View file

@ -78,7 +78,7 @@ def call_plugin(args):
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
args = args) args = args)
proc.communicate() proc.wait()
return proc.returncode == 0 return proc.returncode == 0
@ -169,7 +169,7 @@ def run_cryptsetup(args):
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
args = cs_args) args = cs_args)
proc.communicate() proc.wait()
## chown the devmapper block device to the cryptobox user ## chown the devmapper block device to the cryptobox user
if (proc.returncode == 0) and (action == "luksOpen"): if (proc.returncode == 0) and (action == "luksOpen"):
os.chown(os.path.join(os.path.sep, "dev", "mapper", destination), os.getuid(), os.getgid()) 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( proc = subprocess.Popen(
shell = False, shell = False,
args = [allowedProgs["mount"], device, destination]) args = [allowedProgs["mount"], device, destination])
proc.communicate() proc.wait()
# restore previous real uid ## return in case of an error
os.setuid(savedUID) if proc.returncode != 0:
return 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): def run_umount(args):
@ -242,7 +255,7 @@ def run_umount(args):
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
args = [allowedProgs["umount"], "-l", destination]) args = [allowedProgs["umount"], "-l", destination])
proc.communicate() proc.wait()
# restore previous real uid # restore previous real uid
os.setuid(savedUID) os.setuid(savedUID)
return proc.returncode == 0 return proc.returncode == 0

View file

@ -5,6 +5,7 @@ except:
raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.") raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.")
import os import os
import CryptoBoxExceptions import CryptoBoxExceptions
import subprocess
try: try:
import configobj ## needed for reading and writing of the config file import configobj ## needed for reading and writing of the config file
except: except:
@ -65,6 +66,92 @@ class CryptoBoxSettings:
return ok 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): def __getitem__(self, key):
"""redirect all requests to the 'prefs' attribute""" """redirect all requests to the 'prefs' attribute"""
return self.prefs[key] return self.prefs[key]
@ -282,6 +369,7 @@ Language = string(min=1, default="en")
cryptsetup = fileExecutable(default="/sbin/cryptsetup") cryptsetup = fileExecutable(default="/sbin/cryptsetup")
mkfs-data = fileExecutable(default="/sbin/mkfs.ext3") mkfs-data = fileExecutable(default="/sbin/mkfs.ext3")
blkid = fileExecutable(default="/sbin/blkid") blkid = fileExecutable(default="/sbin/blkid")
blockdev = fileExecutable(default="/sbin/blockdev")
mount = fileExecutable(default="/bin/mount") mount = fileExecutable(default="/bin/mount")
umount = fileExecutable(default="/bin/umount") umount = fileExecutable(default="/bin/umount")
super = fileExecutable(default="/usr/bin/super") super = fileExecutable(default="/usr/bin/super")

View file

@ -5,6 +5,16 @@ import Plugins
from CryptoBoxExceptions import * from CryptoBoxExceptions import *
import cherrypy import cherrypy
import types 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: class WebInterfacePlugins:
@ -115,6 +125,7 @@ class WebInterfaceSites:
def index(self, weblang=""): def index(self, weblang=""):
self.__resetDataset() self.__resetDataset()
self.__setWebLang(weblang) self.__setWebLang(weblang)
self.__checkEnvironment()
## do not forget the language! ## do not forget the language!
param_dict = {"weblang":weblang} param_dict = {"weblang":weblang}
## render "disks" plugin by default ## render "disks" plugin by default
@ -124,6 +135,7 @@ class WebInterfaceSites:
def return_plugin_action(self, plugin): def return_plugin_action(self, plugin):
def handler(self, **args): def handler(self, **args):
self.__resetDataset() self.__resetDataset()
self.__checkEnvironment()
args_orig = dict(args) args_orig = dict(args)
try: try:
self.__setWebLang(args["weblang"]) self.__setWebLang(args["weblang"])
@ -180,6 +192,7 @@ class WebInterfaceSites:
def test(self, weblang=""): def test(self, weblang=""):
self.__resetDataset() self.__resetDataset()
self.__setWebLang(weblang) self.__setWebLang(weblang)
self.__checkEnvironment()
return "test passed" return "test passed"
@ -197,6 +210,36 @@ class WebInterfaceSites:
##################### input checker ########################## ##################### 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): def __setWebLang(self, value):
guess = value guess = value
availLangs = self.cbox.getAvailableLanguages() availLangs = self.cbox.getAvailableLanguages()
@ -229,7 +272,6 @@ class WebInterfaceSites:
"""guess the preferred language of the user (as sent by the browser) """guess the preferred language of the user (as sent by the browser)
take the first language, that is part of 'availLangs' take the first language, that is part of 'availLangs'
""" """
import cherrypy
try: try:
pref_lang_header = cherrypy.request.headers["Accept-Language"] pref_lang_header = cherrypy.request.headers["Accept-Language"]
except KeyError: except KeyError:
@ -277,7 +319,6 @@ class WebInterfaceSites:
def __getLanguageData(self, web_lang="en"): def __getLanguageData(self, web_lang="en"):
import neo_cgi, neo_util, os
default_lang = "en" default_lang = "en"
conf_lang = self.prefs["WebSettings"]["Language"] conf_lang = self.prefs["WebSettings"]["Language"]
hdf = neo_util.HDF() hdf = neo_util.HDF()
@ -302,15 +343,6 @@ class WebInterfaceSites:
def __render(self, renderInfo, plugin=None): def __render(self, renderInfo, plugin=None):
'''renders from clearsilver templates and returns the resulting html '''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? ## is renderInfo a string (filename of the template) or a dictionary?
if type(renderInfo) == types.DictType: if type(renderInfo) == types.DictType:
template = renderInfo["template"] template = renderInfo["template"]