2006-08-23 15:27:25 +02:00
#!/usr/bin/env python2.4
2006-08-16 13:07:57 +02:00
'''
2006-08-17 12:38:05 +02:00
This is the web interface for a fileserver managing encrypted filesystems .
2006-08-16 20:17:16 +02:00
2006-08-16 13:07:57 +02:00
It was originally written in bash / perl . Now a complete rewrite is in
progress . So things might be confusing here . Hopefully not for long .
: )
'''
2006-08-16 20:17:16 +02:00
2006-08-23 15:27:25 +02:00
# check python version
import sys
( ver_major , ver_minor , ver_sub , ver_desc , ver_subsub ) = sys . version_info
if ( ver_major < 2 ) or ( ( ver_major == 2 ) and ( ver_minor < 4 ) ) :
sys . stderr . write ( " You need a python version >= 2.4 \n Current version is: \n %s \n " % sys . version )
sys . exit ( 1 )
2006-08-16 13:07:57 +02:00
import CryptoBoxContainer
2006-09-08 13:02:27 +02:00
from CryptoBoxExceptions import *
2006-08-16 20:17:16 +02:00
import re
import os
2006-09-14 14:33:01 +02:00
import CryptoBoxTools
2006-11-03 15:27:19 +01:00
import subprocess
2006-08-17 12:38:05 +02:00
2006-08-21 09:41:03 +02:00
2006-08-16 09:17:44 +02:00
2006-08-20 18:33:52 +02:00
class CryptoBox :
''' this class rules them all!
put things like logging , conf and oter stuff in here ,
that might be used by more classes , it will be passed on to them '''
2006-09-12 10:55:20 +02:00
VERSION = " 0.3~1 "
2006-08-21 09:41:03 +02:00
def __init__ ( self , config_file = None ) :
2006-09-08 13:02:27 +02:00
import CryptoBoxSettings
self . log = self . __getStartupLogger ( )
self . prefs = CryptoBoxSettings . CryptoBoxSettings ( config_file )
2006-08-23 15:27:25 +02:00
self . __runTests ( )
2006-08-21 09:41:03 +02:00
2006-08-20 18:33:52 +02:00
2006-09-08 13:02:27 +02:00
def __getStartupLogger ( self ) :
2006-08-20 20:23:35 +02:00
import logging
2006-08-20 18:33:52 +02:00
''' initialises the logging system
use it with : ' self.log.[debug|info|warning|error|critical](logmessage) '
2006-08-22 08:55:07 +02:00
all classes should get the logging instance during __init__ :
self . log = logging . getLogger ( " CryptoBox " )
2006-08-20 18:33:52 +02:00
2006-08-22 08:55:07 +02:00
first we output all warnings / errors to stderr
as soon as we opened the config file successfully , we redirect debug output
to the configured destination '''
2006-08-20 18:33:52 +02:00
## basicConfig(...) needs python >= 2.4
2006-08-20 20:23:35 +02:00
try :
2006-09-08 13:02:27 +02:00
log_handler = logging . getLogger ( " CryptoBox " )
2006-08-24 09:36:47 +02:00
logging . basicConfig (
2006-09-25 14:22:41 +02:00
format = ' %(asctime)s CryptoBox %(levelname)s : %(message)s ' ,
2006-08-24 09:36:47 +02:00
stderr = sys . stderr )
2006-09-08 13:02:27 +02:00
log_handler . setLevel ( logging . ERROR )
log_handler . info ( " loggingsystem is up ' n running " )
2006-08-20 20:23:35 +02:00
## from now on everything can be logged via self.log...
except :
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError ( " couldn ' t initialise the loggingsystem. I give up. " )
2006-09-08 13:02:27 +02:00
return log_handler
2006-08-20 18:33:52 +02:00
2006-08-21 09:41:03 +02:00
2006-08-23 15:27:25 +02:00
# do some initial checks
def __runTests ( self ) :
2006-10-11 17:51:28 +02:00
self . __runTestUID ( )
2006-08-27 10:38:48 +02:00
self . __runTestRootPriv ( )
2006-10-11 17:51:28 +02:00
def __runTestUID ( self ) :
if os . geteuid ( ) == 0 :
raise CBEnvironmentError ( " you may not run the cryptobox as root " )
2006-09-05 17:03:16 +02:00
2006-08-27 10:38:48 +02:00
def __runTestRootPriv ( self ) :
""" try to run ' super ' with ' CryptoBoxRootActions ' """
2006-08-23 15:27:25 +02:00
try :
devnull = open ( os . devnull , " w " )
except IOError :
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError ( " could not open %s for writing! " % os . devnull )
try :
2006-09-08 13:02:27 +02:00
prog_super = self . prefs [ " Programs " ] [ " super " ]
2006-08-27 10:38:48 +02:00
except KeyError :
raise CBConfigUndefinedError ( " Programs " , " super " )
try :
2006-09-08 13:02:27 +02:00
prog_rootactions = self . prefs [ " Programs " ] [ " CryptoBoxRootActions " ]
2006-08-27 10:38:48 +02:00
except KeyError :
raise CBConfigUndefinedError ( " Programs " , " CryptoBoxRootActions " )
2006-08-23 18:44:14 +02:00
try :
proc = subprocess . Popen (
shell = False ,
stdout = devnull ,
stderr = devnull ,
2006-08-27 10:38:48 +02:00
args = [ prog_super , prog_rootactions , " check " ] )
2006-08-23 18:44:14 +02:00
except OSError :
2006-09-08 13:02:27 +02:00
raise CBEnvironmentError ( " failed to execute ' super ' ( %s ) " % self . prefs [ " Programs " ] [ " super " ] )
2006-08-23 15:27:25 +02:00
proc . wait ( )
if proc . returncode != 0 :
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError ( " failed to call CryptoBoxRootActions ( %s ) via ' super ' - maybe you did not add the appropriate line to /etc/super.tab? " % prog_rootactions )
2006-08-23 15:27:25 +02:00
# this method just demonstrates inheritance effects - may be removed
2006-08-23 20:19:37 +02:00
def cbx_inheritance_test ( self , string = " you lucky widow " ) :
self . log . info ( string )
2006-08-24 09:36:47 +02:00
2006-08-21 09:41:03 +02:00
# RFC: why should CryptoBoxProps inherit CryptoBox? [l]
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
2006-08-20 18:33:52 +02:00
class CryptoBoxProps ( CryptoBox ) :
2006-08-16 13:07:57 +02:00
''' Get and set the properties of a CryptoBox
2006-08-17 12:38:05 +02:00
This class contains all available devices that may be accessed .
All properties of the cryptobox can be accessed by this class .
2006-08-16 13:07:57 +02:00
'''
2006-08-16 09:17:44 +02:00
2006-08-24 09:36:47 +02:00
def __init__ ( self , config_file = None ) :
2006-08-16 13:07:57 +02:00
''' read config and fill class variables '''
2006-08-24 09:36:47 +02:00
CryptoBox . __init__ ( self , config_file )
2006-09-18 12:16:05 +02:00
self . reReadContainerList ( )
2006-09-14 14:33:01 +02:00
2006-09-18 12:16:05 +02:00
def reReadContainerList ( self ) :
2006-11-06 07:16:27 +01:00
self . log . debug ( " rereading container list " )
2006-08-16 20:17:16 +02:00
self . containers = [ ]
2006-09-14 14:33:01 +02:00
for device in CryptoBoxTools . getAvailablePartitions ( ) :
2006-09-20 12:40:37 +02:00
if self . isDeviceAllowed ( device ) and not self . isConfigPartition ( device ) :
2006-08-17 12:38:05 +02:00
self . containers . append ( CryptoBoxContainer . CryptoBoxContainer ( device , self ) )
2006-09-14 14:33:01 +02:00
## sort by container name
self . containers . sort ( cmp = lambda x , y : x . getName ( ) < y . getName ( ) and - 1 or 1 )
2006-10-09 18:44:35 +02:00
2006-09-20 12:40:37 +02:00
def isConfigPartition ( self , device ) :
proc = subprocess . Popen (
shell = False ,
stdout = subprocess . PIPE ,
args = [
self . prefs [ " Programs " ] [ " blkid " ] ,
" -c " , os . path . devnull ,
" -o " , " value " ,
" -s " , " LABEL " ,
device ] )
( output , error ) = proc . communicate ( )
return output . strip ( ) == self . prefs [ " Main " ] [ " ConfigVolumeLabel " ]
2006-08-16 20:17:16 +02:00
def isDeviceAllowed ( self , devicename ) :
" check if a device is white-listed for being used as cryptobox containers "
2006-09-08 13:02:27 +02:00
import types
allowed = self . prefs [ " Main " ] [ " AllowedDevices " ]
2006-08-17 12:38:05 +02:00
if type ( allowed ) == types . StringType : allowed = [ allowed ]
for a_dev in allowed :
2006-08-18 16:00:49 +02:00
" remove double dots and so on ... "
real_device = os . path . realpath ( devicename )
if a_dev and re . search ( ' ^ ' + a_dev , real_device ) : return True
2006-08-16 20:17:16 +02:00
return False
2006-08-25 09:37:52 +02:00
def getLogData ( self , lines = None , maxSize = None ) :
""" get the most recent log entries of the cryptobox
the maximum number and size of these entries can be limited by ' lines ' and ' maxSize '
"""
# return nothing if the currently selected log output is not a file
try :
2006-09-08 13:02:27 +02:00
if self . prefs [ " Log " ] [ " Destination " ] . upper ( ) != " FILE " : return [ ]
log_file = self . prefs [ " Log " ] [ " Details " ]
2006-08-25 09:37:52 +02:00
except KeyError :
self . log . error ( " could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details " )
2006-08-27 10:38:48 +02:00
return [ ]
2006-08-25 09:37:52 +02:00
try :
fd = open ( log_file , " r " )
2006-09-12 10:55:20 +02:00
if maxSize : fd . seek ( - maxSize , 2 ) # seek relative to the end of the file
content = fd . readlines ( )
2006-08-25 09:37:52 +02:00
fd . close ( )
except IOError :
self . log . warn ( " failed to read the log file ( %s ) " % log_file )
2006-08-27 10:38:48 +02:00
return [ ]
2006-08-25 09:37:52 +02:00
if lines : content = content [ - lines : ]
2006-09-12 10:55:20 +02:00
content . reverse ( )
2006-08-25 09:37:52 +02:00
return content
2006-08-16 20:17:16 +02:00
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 :
2006-08-21 12:10:40 +02:00
self . log . info ( " invalid filterType ( %d ) " % filterType )
2006-08-16 20:17:16 +02:00
result . clear ( )
if filterName != None :
result = [ e for e in self . containers if e . getName ( ) == filterName ]
return result
except AttributeError :
return [ ]
2006-09-07 13:21:56 +02:00
def getContainer ( self , device ) :
" retrieve the container element for this device "
all = [ e for e in self . getContainerList ( ) if e . device == device ]
if all :
return all [ 0 ]
else :
return None
2006-08-16 20:17:16 +02:00
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 "
2006-09-14 14:33:01 +02:00
if used_uuid :
## remember the container which name was overriden
for e in self . containers :
if e . getName ( ) == name :
forcedRename = e
break
del self . prefs . nameDB [ used_uuid ]
2006-09-08 13:02:27 +02:00
self . prefs . nameDB [ uuid ] = name
self . prefs . nameDB . write ( )
2006-09-14 14:33:01 +02:00
## rename the container that lost its name (necessary while we use cherrypy)
if used_uuid :
## this is surely not the best way to regenerate the name
dev = e . getDevice ( )
old_index = self . containers . index ( e )
self . containers . remove ( e )
self . containers . insert ( old_index , CryptoBoxContainer . CryptoBoxContainer ( dev , self ) )
2006-10-09 18:44:35 +02:00
## there should be no reason for any failure
return True
2006-08-16 20:17:16 +02:00
def getNameForUUID ( self , uuid ) :
" get the name belonging to a specified key (usually the UUID of a fs) "
try :
2006-09-08 13:02:27 +02:00
return self . prefs . nameDB [ uuid ]
2006-08-16 20:17:16 +02:00
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 ' """
2006-09-08 13:02:27 +02:00
for key in self . prefs . nameDB . keys ( ) :
if self . prefs . nameDB [ key ] == name : return key
2006-08-16 20:17:16 +02:00
" the uuid was not found "
return None
2006-11-06 07:16:27 +01:00
def removeUUID ( self , uuid ) :
if uuid in self . prefs . nameDB . keys ( ) :
del self . prefs . nameDB [ uuid ]
return True
else :
return False
2006-08-16 20:17:16 +02:00
2006-09-05 17:03:16 +02:00
2006-08-30 21:56:37 +02:00
def getAvailableLanguages ( self ) :
''' reads all files in path LangDir and returns a list of
basenames from existing hdf files , that should are all available
languages '''
2006-11-03 15:27:19 +01:00
languages = [ f . rstrip ( " .hdf " )
for f in os . listdir ( self . prefs [ " Locations " ] [ " LangDir " ] )
if f . endswith ( " .hdf " ) ]
2006-08-30 21:56:37 +02:00
if len ( languages ) < 1 :
2006-09-25 14:22:41 +02:00
self . log . error ( " No .hdf files found! The website won ' t render properly. " )
2006-08-30 21:56:37 +02:00
return languages
2006-09-05 17:03:16 +02:00
2006-08-16 20:17:16 +02:00
2006-11-06 07:16:27 +01:00
2006-08-18 16:00:49 +02:00
if __name__ == " __main__ " :
2006-11-03 15:27:19 +01:00
cb = CryptoBoxProps ( )
2006-08-18 16:00:49 +02:00